Nachdem wir uns im letzten Monat bereits mit
- Raspberry Pi OS installieren und einrichten
- Installation Hadoop Cluster und
- Installation Doris Cluster
beschäftigt haben, wollen wir unsere Wetter- und Wärmepumpendaten nun in eine DorisDB schreiben und von dort mit Superset auswerten.
Synchronisieren der MariaDB Daten in unsere DorisDB
Die Wetter- und Wärmepumpendaten werden weiterhin in unsere MariaDB geschrieben, wir werden sie jetzt zusätzlich in unsere DorisDB kopieren und regelmäßig kopieren lassen.
Wir könnten die Daten auch direkt in unsere DorisDB schreiben lassen – durch das Kopieren haben wir die Daten aber lokal, müssen nicht über das Netzwerk abfragen und quasi ein Backup.
Um auf die MariaDB zugreifen zu können, können wir praktischerweise den MySQL J Connector verwenden und in Doris einbinden.
Mysql J Connector runterladen und in Doris einbinden
- cd
- wget -c https://cdn.mysql.com//Downloads/Connector-J/mysql-connector-j-8.3.0.tar.gz
- mkdir temp
- cd temp/
- tar xpf ../mysql-connector-j-8.3.0.tar.gz
- cd /opt/doris/
- mkdir jdbc_drivers
- cp ~/mysql-connector-j-8.3.0/mysql-connector-j-8.3.0.jar jdbc_drivers/
- scp -rp jdbc_drivers pi4b01:/opt/doris/
- scp -rp jdbc_drivers pi4b02:/opt/doris/
- cd
- rm -rf mysql-connector-j-8.3.0
MariaDB Catalog in Doris einrichten
mysql -h pi5b -P 9030 -uroot
CREATE CATALOG asusn100_mysql PROPERTIES (
„type“=“jdbc“,
„user“=“bme280“,
„password“=“bme280“,
„jdbc_url“ = „jdbc:mysql://192.168.42.10:3306/bme280“,
„driver_url“ = „mysql-connector-j-8.3.0.jar“,
„driver_class“ = „com.mysql.cj.jdbc.Driver“
);
SHOW CATALOGS;
Doris Datenbank/User anlegen und MariaDB Zugriff testen
create database bme280;
CREATE USER ‚bme280‘ IDENTIFIED BY ‚bme280‘;
GRANT ALL ON bme280.* TO ‚bme280‘;
GRANT SELECT_PRIV ON asusn100_mysql.bme280.* TO ‚bme280‘;
select max(Messzeit) from asusn100_mysql.bme280.wohnzimmer;
Doris Tabellen anlegen und befüllen
Analog zu unseren MariaDB Tabellen legen wir die Tabellen in unserer DorisDB an und befüllen sie initial mit dem aktuellen Stand.
CREATE TABLE bme280.wohnzimmer (
Messzeit DATETIME,
Temperatur DECIMAL(6 , 2),
Druck DECIMAL(6 , 2) ,
Luftfeuchtigkeit DECIMAL(6 , 2 )
)
UNIQUE KEY (Messzeit)
DISTRIBUTED BY HASH(Messzeit) BUCKETS 1
PROPERTIES(„replication_num“ = „2“);
insert into bme280.wohnzimmer select * from asusn100_mysql.bme280.wohnzimmer;
CREATE TABLE bme280.terrasse (
Messzeit DATETIME,
Temperatur DECIMAL(6 , 2),
Druck DECIMAL(6 , 2) ,
Luftfeuchtigkeit DECIMAL(6 , 2 )
)
UNIQUE KEY (Messzeit)
DISTRIBUTED BY HASH(Messzeit) BUCKETS 1
PROPERTIES(„replication_num“ = „2“);
insert into bme280.terrasse select * from asusn100_mysql.bme280.terrasse;
CREATE TABLE bme280.eltern (
Messzeit DATETIME,
Temperatur DECIMAL(6 , 2),
Druck DECIMAL(6 , 2) ,
Luftfeuchtigkeit DECIMAL(6 , 2 )
)
UNIQUE KEY (Messzeit)
DISTRIBUTED BY HASH(Messzeit) BUCKETS 1
PROPERTIES(„replication_num“ = „2“);
insert into bme280.eltern select * from asusn100_mysql.bme280.eltern;
CREATE TABLE bme280.waermepumpe (
Ablesedatum DATE,
kwh DECIMAL(10, 2)
)
UNIQUE KEY (Ablesedatum)
DISTRIBUTED BY HASH(Ablesedatum) BUCKETS 1
PROPERTIES(„replication_num“ = „2“);
insert into bme280.waermepumpe select * from asusn100_mysql.bme280.waermepumpe;
MariaDB Daten inkrementell kopieren lassen
Hierfür legen wir einen crontab Eintrag an und lassen ihn alle 5 Minuten laufen
*/5 * * * * /home/hduser/bme280_copy_from_mysql.sh > /dev/null 2>&1
Scriptinhalt von bme280_copy_from_mysql.sh
#!/bin/bash
source /home/hduser/.profile
# check for running DorisFE processes
if [ $(jps |grep DorisFE |wc -l) -lt 1 ]
then
exit 0;
fi
sleep 5
mysql -h pi5b -P 9030 -ubme280 -pbme280 -e ‚insert into bme280.wohnzimmer select * from asusn100_mysql.bme280.wohnzimmer where Messzeit > (select max(Messzeit) from bme280.wohnzimmer);‘
mysql -h pi5b -P 9030 -ubme280 -pbme280 -e ‚insert into bme280.terrasse select * from asusn100_mysql.bme280.terrasse where Messzeit > (select max(Messzeit) from bme280.terrasse);‘
mysql -h pi5b -P 9030 -ubme280 -pbme280 -e ‚insert into bme280.eltern select * from asusn100_mysql.bme280.eltern where Messzeit > (select max(Messzeit) from bme280.eltern);‘
mysql -h pi5b -P 9030 -ubme280 -pbme280 -e ‚insert into bme280.waermepumpe select * from asusn100_mysql.bme280.waermepumpe where Ablesedatum > (select max(Ablesedatum) from bme280.waermepumpe);‘
Installation Superset
Benötigte Packages installieren
- sudo apt-get install build-essential libssl-dev libffi-dev python3-dev python3-pip libsasl2-dev libldap2-dev
- sudo apt-get install python3-venv
MariaDB installieren (für Superset Metastore) und einrichten
- sudo apt-get install mariadb-server
- sudo mysql_secure_installation
- sudo mysql -uroot -p
mysql> GRANT ALL PRIVILEGES on *.* to ‚root’@’localhost‘ IDENTIFIED BY ’superset‘;
mysql> FLUSH PRIVILEGES;
mysql> exit;
- mysql_tzinfo_to_sql /usr/share/zoneinfo | sudo mysql -uroot -psupserset mysql
- sudo vi /etc/mysql/mariadb.conf.d/50-server.cnf
[mysqld]
default_time_zone = Europe/Vienna
bind-address = 192.168.42.33
- sudo systemctl restart mysql
Superset Python Environment anlegen
- cd
- python3 -m venv superset
- . superset/bin/activate
- pip install –upgrade setuptools pip
Superset & zusätzliche Pakete mit pip installieren
- pip install Pillow
- pip install gevent
- pip install apache-superset
- deactivate
Superset MariaDB User anlegen und Berechtigungen vergeben
- mysql -uroot -psuperset
CREATE DATABASE superset DEFAULT CHARACTER SET utf8;
CREATE USER ’superset’@localhost IDENTIFIED BY ’superset‘;
GRANT all on superset.* to ’superset’@localhost identified by ’superset‘;
flush privileges;
Superset Configuration anlegen
- cd
- mkdir -p .superset
- vi .superset/superset_config.py
# Superset specific config
#———————————————————
ROW_LIMIT = 100000
SUPERSET_WORKERS = 2
SUPERSET_WEBSERVER_PORT = 18088
#———————————————————
#———————————————————
# Flask App Builder configuration
#———————————————————
# Your App secret key
SECRET_KEY = ’supersupersetsecret‘
# The SQLAlchemy connection string to your database backend
# This connection defines the path to the database that stores your
# superset metadata (slices, connections, tables, dashboards, …).
# Note that the connection information to connect to the datasources
# you want to explore are managed directly in the web UI
#SQLALCHEMY_DATABASE_URI = ’sqlite:////path/to/superset.db‘
SQLALCHEMY_DATABASE_URI = ‚mysql://superset:superset@localhost:3306/superset‘
#SQLLAB Timout auf 1h
SQLLAB_TIMEOUT = 36000
# Flask-WTF flag for CSRF
WTF_CSRF_ENABLED = False
# Add endpoints that need to be exempt from CSRF protection
WTF_CSRF_EXEMPT_LIST = []
# A CSRF token that expires in 1 year
WTF_CSRF_TIME_LIMIT = 60 * 60 * 24 * 365
# Set this API key to enable Mapbox visualizations
MAPBOX_API_KEY = “
# OAuth login broken with Content Security Policy
TALISMAN_ENABLED = False
Superset mit MariaDB Metastore einrichten
- . superset/bin/activate
- sudo apt-get install libmariadb-dev
- pip install mysqlclient
- deactivate
Systemd Service für Superset anlegen
- sudo vi /etc/systemd/system/superset.service
[Unit]
Description=Superset
After=multi-user.target
[Service]
Type=simple
User=hduser
Environment=SUPERSET_CONFIG_PATH=/home/hduser/.superset/superset_config.py
Environment=PYTHONPATH=/home/hduser/superset
Environment=FLASK_APP=superset
WorkingDirectory = /home/hduser/superset
ExecStart=/home/hduser/superset/bin/gunicorn -w 4 -k gevent –worker-connections 20 –timeout 120 -b 0.0.0.0:18088 –limit-request-line 0 –limit-request-field_size 0 „superset.app:create_app()“
[Install]
WantedBy=default.target
Übernahme der vorhandenen Superset Dashboards + Anpassungen
Da wir denselben Superset SECRET_KEY in unserer superset_config.py verwenden, können wir die DB von unserem pi3b übernehmen und auf unserem pi5b einspielen
- mariadb-dump -usuperset -psuperset -h192.168.42.43 superset > mysql.superset.sql
- mysql -usuperset -psuperset -Dsuperset < mysql.superset.sql
Zusätzlich installieren wir noch den Doris Connector für Superset
- . superset/bin/activate
- pip install pydoris
- deactivate
Danach können wir Superset auch schon starten
- sudo systemctl start superset
In Superset können wir jetzt eine neue Datenquelle anlegen mit „Apache Doris“, oder bei der vorhandenen Datenquelle einfachen den Namen und „SQLALCHEMY URI“ umschreiben:
Generell müssen wir unsere Temperatur Dataset’s anpassen – bei unserer MariaDB hatten wir einfach ein „select * from“ gemacht und den Rest konnten wir dann einfach in Superset zusammenklicken.
Um jetzt keine Syntaxfehler zu erhalten, müssen wir unser SQL‘s anpassen und Metrics anlegen damit wir die Felder dann auch entsprechend benennen können:
Nach diesen kleinen Anpassungen, werden unsere Wetterdaten Charts wieder korrekt angezeigt, wenn wir unsere Metrics verwenden.
Bei den Wärmepumpendaten müssen wir unsere View #1 anpassen, weil es die MariaDB Funktion ROWNUM() in Doris nicht gibt – das nutzen wir gleich aus und lassen uns hier gleich die Heizsaison mitberechnen, zusätzliche werden wir die Monate auf Deutsch anzeigen lassen:
create view waermepumpe_view as
(
select hzs.id,hzs.Ablesedatum,hzs.kwh,
case hzs.Monat
when ‚January‘ then ‚Jänner‘
when ‚February‘ then ‚Februar‘
when ‚March‘ then ‚März‘
when ‚April‘ then ‚April‘
when ‚May‘ then ‚Mai‘
when ‚June‘ then ‚Juni‘
when ‚July‘ then ‚Juli‘
when ‚August‘ then ‚August‘
when ‚September‘ then ‚September‘
when ‚October‘ then ‚Oktober‘
when ‚November‘ then ‚November‘
when ‚December‘ then ‚Dezember‘
Else ‚Fehler‘
end as Monat,
case Monat
when ‚September‘ then concat(right(Jahr,2),’/‘,right(Jahr+1,2))
when ‚October‘ then concat(right(Jahr,2),’/‘,right(Jahr+1,2))
when ‚November‘ then concat(right(Jahr,2),’/‘,right(Jahr+1,2))
when ‚December‘ then concat(right(Jahr,2),’/‘,right(Jahr+1,2))
else concat(right(Jahr-1,2),’/‘,right(Jahr,2))
end as Heizsaison,
hzs.Jahr,hzs.kwh_monat,
hzs.kwh_monat-hzs2.kwh_monat as ‚Diff_Vorjahr‘,
concat(round((hzs.kwh_monat/hzs2.kwh_monat-1)*100,2),’%‘) as ‚Diff_Vorjahr_Prozent‘
from
(
select ROW_NUMBER() over(partition by left(all1.Ablesedatum,1) order by all1.Ablesedatum) as id,all1.Ablesedatum,all1.kwh,
date_format(DATE_SUB(all1.Ablesedatum, interval 1 day),’%M‘) as ‚Monat‘,
date_format(DATE_SUB(all1.Ablesedatum, interval 1 day),’%Y‘) as ‚Jahr‘,
all1.kwh-all2.kwh as kwh_monat
from
(
select ROW_NUMBER() over(partition by left(Ablesedatum,1) order by Ablesedatum) as id, Ablesedatum, kwh from bme280.waermepumpe where date_format(Ablesedatum,’%d‘) in (’01‘) order by Ablesedatum asc
) as all1
left outer join
(
select ROW_NUMBER() over(partition by left(Ablesedatum,1) order by Ablesedatum)+1 as id, Ablesedatum, kwh from bme280.waermepumpe where date_format(Ablesedatum,’%d‘) in (’01‘) order by Ablesedatum asc
) as all2
on all1.id=all2.id
) hzs
left outer join
(
select ROW_NUMBER() over(partition by left(all1.Ablesedatum,1) order by all1.Ablesedatum)+12 as id,all1.Ablesedatum,all1.kwh,all1.kwh-all2.kwh as kwh_monat
from
(
select ROW_NUMBER() over(partition by left(Ablesedatum,1) order by Ablesedatum) as id, Ablesedatum, kwh from bme280.waermepumpe where date_format(Ablesedatum,’%d‘) in (’01‘) order by Ablesedatum asc
) as all1
left outer join
(
select ROW_NUMBER() over(partition by left(Ablesedatum,1) order by Ablesedatum)+1 as id, Ablesedatum, kwh from bme280.waermepumpe where date_format(Ablesedatum,’%d‘) in (’01‘) order by Ablesedatum asc
) as all2
on all1.id=all2.id
order by all1.Ablesedatum asc
) hzs2
on hzs.id=hzs2.id
)
;
Beim Dataset für unser Chart WP_1 können wir dann die Heizsaisonberechnung weglassen und das SQL ein bisschen entschlacken:
select hzs.Heizsaison,
sep.kwh_Monat as ‚September‘,
okt.kwh_Monat as ‚Oktober‘,
nov.kwh_Monat as ‚November‘,
dez.kwh_Monat as ‚Dezember‘,
jan.kwh_Monat as ‚Jänner‘,
feb.kwh_Monat as ‚Februar‘,
mar.kwh_Monat as ‚März‘,
apr.kwh_Monat as ‚April‘,
mai.kwh_Monat as ‚Mai‘,
jun.kwh_Monat as ‚Juni‘,
jul.kwh_Monat as ‚Juli‘,
aug.kwh_Monat as ‚August‘
from
(select distinct Heizsaison from waermepumpe_view
order by 1
) as hzs
left outer join (select * from waermepumpe_view where Monat=’Jänner‘) jan on hzs.Heizsaison=jan.Heizsaison
left outer join (select * from waermepumpe_view where Monat=’Februar‘) feb on hzs.Heizsaison=feb.Heizsaison
left outer join (select * from waermepumpe_view where Monat=’März‘) mar on hzs.Heizsaison=mar.Heizsaison
left outer join (select * from waermepumpe_view where Monat=’April‘) apr on hzs.Heizsaison=apr.Heizsaison
left outer join (select * from waermepumpe_view where Monat=’Mai‘) mai on hzs.Heizsaison=mai.Heizsaison
left outer join (select * from waermepumpe_view where Monat=’Juni‘) jun on hzs.Heizsaison=jun.Heizsaison
left outer join (select * from waermepumpe_view where Monat=’Juli‘) jul on hzs.Heizsaison=jul.Heizsaison
left outer join (select * from waermepumpe_view where Monat=’August‘) aug on hzs.Heizsaison=aug.Heizsaison
left outer join (select * from waermepumpe_view where Monat=’September‘) sep on hzs.Heizsaison=sep.Heizsaison
left outer join (select * from waermepumpe_view where Monat=’Oktober‘) okt on hzs.Heizsaison=okt.Heizsaison
left outer join (select * from waermepumpe_view where Monat=’November‘) nov on hzs.Heizsaison=nov.Heizsaison
left outer join (select * from waermepumpe_view where Monat=’Dezember‘) dez on hzs.Heizsaison=dez.Heizsaison
where sep.kwh_Monat is not null
order by hzs.Heizsaison;
Zusätzlich legen wir uns die Metrics für die einzelnen Monate an und verwenden sie in unserem Chart.
Unser Dataset für unser Chart WP_2 können wir ebenfalls ein bisschen vereinfachen:
select monate.Monat
,hzs2122.kwh_Monat as ‚Heizsaison_21_22‘
,hzs2223.kwh_Monat as ‚Heizsaison_22_23‘
,hzs2324.kwh_Monat as ‚Heizsaison_23_24‘
from
(select distinct Monat as Monat from waermepumpe_view) as monate
left outer join
(select * from waermepumpe_view
) as hzs2122 on monate.Monat=hzs2122.Monat and hzs2122.Heizsaison=’21/22′
left outer join
(select * from waermepumpe_view
) as hzs2223 on monate.Monat=hzs2223.Monat and hzs2223.Heizsaison=’22/23′
left outer join
(select * from waermepumpe_view
) as hzs2324 on monate.Monat=hzs2324.Monat and hzs2324.Heizsaison=’23/24′
order by
case Monat
when ‚September‘ then 1
when ‚Oktober‘ then 2
when ‚November‘ then 3
when ‚Dezember‘ then 4
when ‚Jänner‘ then 5
when ‚Februar‘ then 6
when ‚März‘ then 7
when ‚April‘ then 8
when ‚Mai‘ then 9
when ‚Juni‘ then 10
when ‚Juli‘ then 11
when ‚August‘ then 12
else 99
end;
Auch hier legen wir wieder Metrics für die Heizsaisons an:
Fazit
Wir haben unser Vorhaben erfolgreich umgesetzt, unser Cluster läuft und kopiert regelmäßig die Daten von der MariaDB in unsere DorisDB.
Zusätzlich haben wir auch ein Hadoop-Grundgerüst bei Bedarf am Laufen und können hier auch zukünftig neue Tools ausprobieren.
Die Umstellung von MariaDB auf DorisDB war in Hinsicht auf unsere Superset Charts und Dashboards nicht ganz so einfach wie gedacht, allerdings hielt sich hier der Aufwand auch in Grenzen (sobald wir den Dreh raushatten, was wir wie anpassen müssen)
Für unsere Zwecke ist so ein Cluster natürlich übertrieben, aber das Gute ist, dass wir einfach die MicroSD Karten tauschen können und unseren Raspberry Pi 5 als SingleNode nur mit DorisDB & Superset betreiben können – die nötige Vorarbeit haben wir ja schon geleistet.