j4n
j4n.e7h.eu
e7h

2025-12-11 20:30:00

Smart Green Home: Die Monitoring-Entwicklungsumgebung

Nachdem nun die Hardware steht und erste Daten in Experimenten »angezapft« werden konnten, ist es nun an der Zeit, in die Entwicklung eines lokalen Monitorings einzusteigen. Dazu braucht es eine verlässliche und reproduzierbare Entwicklungsumgebung.

In der Vergangenheit habe ich bereits gute Erfahrungen mit Devcontainern gemacht. Sie bieten konsistente und reproduzierbare Entwicklungsumgebungen auf allen gängigen Betriebssystemen und können gleich ganze Tech Stacks abbilden. Teilweise habe ich davon in diesem Blog geschrieben.

Mit diesem Teil wird es für mehrere Teile super technisch und möglicherweise etwas zu »trocken« für den einen oder anderen Leser. Aber irgendwann kommen noch weitere Erfahrungsberichte – denn in der Zwischenzeit ist viel passiert.


Was wird gebraucht?

Ich habe mir vorgenommen, Daten mit einem oder mehreren in Go geschriebenen Programmen zu erfassen und in eine InfluxDB zu schreiben. Grafana möchte ich dazu nutzen, die gesammelten Daten zu visualisieren, Alarme zu definieren und Benachrichtigungen zu versenden.

Warum Go?

»Go« ist aktuell neben Python eine meiner Lieblingsprogrammiersprachen. Sie ist ähnlich universell einsetzbar wie Python und verfügt über ein ausgezeichnetes Ökosystem. Außerdem lassen sich fertig kompilierte Go-Programme mit sehr wenig Aufwand und sehr überschaubaren Abhängigkeiten in der späteren Betriebsumgebung ausführen.

Alle Aufgaben zur Sammlung von Daten hätten sich genauso gut mit Python oder anderen Sprachen realisieren lassen.

In unserem Devcontainer-Setup sollte im Optimalfall der gesamte Stack aus einem Container für die Go-Entwicklung, Grafana und InfluxDB enthalten sein. Dies kann erreicht werden, indem eine docker-compose.yml für den Devcontainer hinterlegt wird.

Konkreter Aufbau der Entwicklungsumgebung

Beginnen wir mit wichtigen Dateien für den Aufbau der Devcontainer-basierten Entwicklungsumgebung. Zur Übersicht organisiere ich meine Devcontainer-Setups in der Regel im Verzeichnis .devcontainer/ direkt unterhalb des Projekt-Roots, statt alle Dateien (inklusive einer .devcontainer.json) im Projekt-Root zu haben. Gängige IDEs, aber auch Helfer-Tools wie beispielsweise DevPod und GitHub Codespaces können problemlos damit umgehen.

Die Basis: .devcontainer/devcontainer.json

Ausgangspunkt ist diese .devcontainer/devcontainer.json, die die Docker-Compose-Datei referenziert und den darin enthaltenen Service devcontainer als primären Entwicklungscontainer ausweist.

 1{
 2  "name": "energy-monitor",
 3  "dockerComposeFile": "docker-compose.yml",
 4  "service": "devcontainer",
 5  "workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}",
 6  "forwardPorts": [
 7    "influx:8086",
 8    "grafana:3000"
 9  ]
10}

Zusätzlich werden bereits hier die weiterleiteten Ports für die InfluxDB-Datenbank und die Grafana-Instanz definiert, damit auf diese Dienste auch von außerhalb des Containers zugegriffen werden kann.

Außerdem wäre es möglich, hier bereits Anpassungen für die IDE vorzunehmen. Ich verwende GoLand als IDE – die Anpassungen werden jedoch im .idea-Ordner im Projekt gespeichert und mit eingecheckt.

Der Stack: .devcontainer/docker-compose.yml

Hier finden sich nun die drei besprochenen Komponenten devcontainer, grafana und influx des Stacks wieder:

 1services:
 2
 3  devcontainer:
 4    container_name: em-devcontainer
 5    build: 
 6      context: .
 7      dockerfile: Dockerfile
 8    volumes:
 9      - ../..:/workspaces:cached
10    depends_on:
11      - influx
12    networks:
13      - em-net
14    environment:
15      EM_INFLUXDB_HOST: http://influx:8086
16      EM_INFLUXDB_ORG: developer
17      EM_INFLUXDB_BUCKET: energy
18      EM_INFLUXDB_TOKEN: DeveloperInfluxAdminToken==
19    command: sleep infinity
20
21  grafana:
22    container_name: em-grafana
23    image: grafana/grafana-oss
24    restart: unless-stopped
25    ports:
26      - "3000:3000"
27    networks:
28      - em-net
29    depends_on:
30      - influx
31    environment:
32      GF_DATABASE_TYPE: sqlite3
33      INFLUXDB_HOST: http://influx:8086
34      INFLUXDB_ORG: developer
35      INFLUXDB_BUCKET: energy
36      INFLUXDB_TOKEN_FILE: /run/secrets/influxdb2-admin-token
37    secrets:
38      - influxdb2-admin-token
39
40  influx:
41    container_name: em-influx
42    image: influxdb:2
43    restart: unless-stopped
44    networks:
45      - em-net
46    environment:
47      DOCKER_INFLUXDB_INIT_MODE: setup
48      DOCKER_INFLUXDB_INIT_ORG: developer
49      DOCKER_INFLUXDB_INIT_BUCKET: energy
50      DOCKER_INFLUXDB_INIT_USERNAME_FILE: /run/secrets/influxdb2-admin-username
51      DOCKER_INFLUXDB_INIT_PASSWORD_FILE: /run/secrets/influxdb2-admin-password
52      DOCKER_INFLUXDB_INIT_ADMIN_TOKEN_FILE: /run/secrets/influxdb2-admin-token
53    secrets:
54      - influxdb2-admin-username
55      - influxdb2-admin-password
56      - influxdb2-admin-token
57    ports:
58      - "8086:8086"
59
60secrets:
61  influxdb2-admin-username:
62    file: .env.influxdb2-admin-username
63  influxdb2-admin-password:
64    file: .env.influxdb2-admin-password
65  influxdb2-admin-token:
66    file: .env.influxdb2-admin-token
67
68networks:
69  em-net:
70    name: em-net

Hier ergeben sich eine kleine Reihe an Besonderheiten:

  • Verwendung von InfluxDB Version 2: Ich bin persönlich ein großer Fan der Flux-Abfragesprache für die Zeitreihen-Datenbank, da man mit den Abfragen gleich bestimmte Datenaufbereitungen verbinden kann. Leider steht »Flux« ab Version 3 nicht mehr zur Verfügung1.
  • Verwendung von Docker Secrets in einem lokalen Entwicklungscontainer: Die Standard-Images von Grafana2 und InfluxDB3 verwenden Docker Secrets
  • Referenz auf Dockerfile für Hauptentwicklungscontainer: Der Container braucht einige Anpassungen, weswegen ich auf einen eigenen Dockerfile statt auf ein fertiges Image setze.
InfluxDB 2: Technische Schulden gleich zu Beginn?

In der Tat baue ich hier direkt von Anfang an bewusst technische Schulden auf. Zu dem Zeitpunkt, zu dem ich das Monitoring an den Start bringen wollte, hatte ich mich noch nicht in die Alternativen zu »Flux« eingearbeitet und mir gedacht, dass eine Migration auf InfluxDB 3 im Nachgang noch machbar wird, möglicherweise sogar mit der Hilfe von KI (Looking at you, JetBrains AI).

Klassische Trade-Off-Betrachtung: »Neuester Tech Stack« versus »mit bekanntem Tech Stack (vermeintlich) schneller zum Release kommen«.

Was daraus geworden ist und welche Herausforderungen damit verbunden waren und es noch sind, werde ich in einem späteren Artikel beschreiben.

Soviel vorweg: Die beste Entscheidung war das sicher nicht.

Secrets in den .devcontainer/.env.influxdb2-admin-*-Dateien

Secrets einchecken oder nicht?

Einfache Antwort: Nein, nicht einchecken, niemals. Und schon gar nicht ungeschützt bzw. unverschlüsselt.

Doch für diesen Devcontainer ist es eine komfortable Lösung, zumal die Credentials sowieso an anderer Stelle der docker-compose.yml-Datei vorhanden sind und es sich um reine Dev-Instanzen handelt.

Hier gibt es auf jeden Fall Potenzial für Optimierungen. Für das heimische Entwicklungssetup ist das jedoch ausreichend.

Die drei Dateien enthalten einfach nur die Entwicklungs-Credentials:

Datei im Ordner .devcontainer/ Inhalt
.env.influxdb2-admin-username developer
.env.influxdb2-admin-password developer
.env.influxdb2-admin-token DeveloperInfluxAdminToken==

Der Hauptentwicklungscontainer: .devcontainer/Dockerfile

Den eigentlichen Entwicklungscontainer, also den Container, in dem die Entwicklung des Go-Codes stattfindet, wird als Dockerfile auf Basis des Standard-golang-Containers definiert, um die für mich erwünschten Anpassungen durchführen zu können. Ich verwende die bookworm-Variante4.

 1FROM golang:1.25-bookworm
 2
 3ENV DEBIAN_FRONTEND=noninteractive
 4
 5RUN apt-get -y update && apt-get -y upgrade
 6RUN apt-get -y install make build-essential crossbuild-essential-amd64 wget curl bash jq less
 7
 8RUN curl https://sh.rustup.rs -sSf | sh -s -- -y
 9RUN (. "$HOME/.cargo/env" && cargo install just)
10RUN (. "$HOME/.cargo/env" && cargo install starship)
11RUN echo 'eval "$(starship init bash)"' >> "$HOME/.bashrc"
12
13RUN go install github.com/go-delve/delve/cmd/dlv@latest
14RUN go install github.com/segmentio/golines@latest
15RUN go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@latest
16RUN go install golang.org/x/vuln/cmd/govulncheck@latest

Neben einigen Build-Tools wird im Container noch eine Rust-Umgebung installiert. Ursprünglich hatte ich geplant, einige Teile in Rust umzusetzen, mich aber im Verlauf der Entwicklung dagegen entschieden. Sie bleibt trotzdem Bestandteil des Containers, da ich so noch zwei Tools bereitstellen kann, die ich sehr gerne nutze:

  • starship5: Ein sehr hilfreiches Prompt für die Shell (hier für die Bash)
  • just6: Ein Command Runner, ähnlich make, jedoch etwas einfacher in der Handhabung und meiner Meinung nach praktischer in der Anwendung

Die übrigen go install-Aufrufe installieren den Debugger und ein paar Tools für statische Codeanalyse. Deren Einsatz erläutere ich zu einem späteren Zeitpunkt.

Der größte Vorteil an diesem Container-Setup für mich: In diesem Container kann ich auf meinem MacBook problemlos eine Version kompilieren, die besonders gut zur Zielbetriebsumgebung passt. Mehr dazu zu einem späteren Zeitpunkt.

Den Container-Stack hochfahren

Persönlich bevorzuge ich bei der Entwicklung mit Devcontainern auf meinem MacBook die Kombination von Podman und DevPod. Als IDE setze ich – wie oben schon beschrieben – auf JetBrains GoLand. DevPod startet den Container direkt auf Basis des Projektverzeichnisses und konfiguriert alles, damit über das JetBrains Gateway GoLand verwendet werden kann. Sehr praktisch!

Nach kurzer Zeit sollte die IDE geöffnet sein, mit einem noch leeren Projekt.

Ein schneller Check

Mit dem Start des Devcontainers kann es endlich losgehen. Der beste Zeitpunkt, den Stack auf grundlegende Funktion zu prüfen:

InfluxDB Grafana
InfluxDB-Login Grafana-Login
URL: http://localhost:8086/ http://localhost:3000/
Benutzername: developer admin
Passwort: developer admin7

Hello World!

Auch die eigentliche Umgebung im Devcontainer wollen wir jetzt testen. Dazu nutzen wir das Terminal der IDE und erstellen eine neue Go-Mod-Konfiguration:

1go mod init j4n.e7h.eu/dev/energy/em-collect

Anschließend erzeugen wir im Projekt-Root eine Datei main.go mit diesem Inhalt:

1package main
2
3import (
4    "fmt"
5)
6
7func main() {
8    fmt.Println("hello, world!")
9}

Neben der main-Funktion erscheint direkt ein »Play«-Button. Wir klicken einmal darauf, und in der Konsole sollte fröhlich »hello, world!« erscheinen:

»Hello World« in der Ausführungskonsole von GoLand

Nun ist alles vorbereitet.

Ein kleiner Ausblick auf das Betriebskonzept

Es werden eine Menge an Daten erfasst werden, und ich möchte diese Daten wenigstens zwei Jahre speichern können. Ich habe mich dazu entschieden, ein preiswertes 2-Platten-NAS-System zu erwerben, welches nicht nur die Festplatten im RAID1-Modus betreiben kann, sondern auch in der Lage ist, Docker-Container auszuführen.

So kann ich auf diesem NAS Grafana und InfluxDB als Container betreiben, und das Go-Programm zur Datensammlung in einem Photon-Container laufen lassen. So muss ich keine eigenen Container bauen, sondern kann den bestehenden minimalistischen Photon-Container verwenden. Es wird dazu ein Verzeichnis in den Container als /em gemountet, welches neben der Executable auch die passenden Konfigurationsdateien und ein Startskript enthält. Als Ausführungsbefehl wird dann /bin/bash /em/startup.sh für den Container konfiguriert.

Das Startup-Skript wird dann in etwa so aussehen, wenn unser Programm em-collect heißt:

1#!/bin/bash
2
3cd /em || exit 1
4chmod +x em-collect
5
6./em-collect

Und so geht es weiter

Im nächsten Artikel werde ich dann konkret in der Implementierung des Go-Programms em-collect beginnen: Eine erste Datenquelle, der Smart Meter vom Netzbetreiber, frisch gesetzt im Zuge der Inbetriebnahme der PV-Anlage, wird periodisch abgefragt und die Messwerte in der InfluxDB gespeichert. Mit einem Tasmota-betriebenen Infrarot-Tastkopf sollte das eigentlich ein Kinderspiel werden.

Welche Hürden dennoch zu überwinden waren und warum gerade diese Daten so echtzeitnah wie möglich wichtig sind, um erste Steuerungsaufgaben umzusetzen, wird ebenfalls Bestandteil des nächsten Teils.

Interessant?

Ich freue mich, per E-Mail von Dir zu hören, wenn Du eigene Erfahrungen diskutieren möchtest oder gerade selbst ein ähnliches Projekt planst.


Hinweis zur KI-Nutzung

Dieser Artikel wurde von einem Menschen geschrieben, anschließend mit Hilfe von KI korrigiert und teilweise umformuliert.

KI hat keinerlei Inhalte selbst erzeugt oder Fakten beigetragen.


  1. Die Abfragesprache »Flux« befindet sich offiziell im »Maintenance Mode«, siehe The future of Flux ↩︎

  2. Siehe »Use Grafana with InfluxDB OSS« ↩︎

  3. Siehe »Install InfluxDB using Docker Compose« ↩︎

  4. Das offizielle Docker-Go-Image in der Variante bookworm basiert auf Debian 12 (Codename bookworm). Zum Zeitpunkt dieses Artikels ist das die »oldstable« Version von Debian. ↩︎

  5. Offizielle Starship-Website: https://starship.rs/ ↩︎

  6. Offizielle Just-Website: https://just.systems/ ↩︎

  7. Grafana verlangt bei der ersten Anmeldung die Änderung des Passworts. Es ist jedoch möglich, das neue Passwort wieder auf admin zu setzen – jedoch wird beim nächsten Login erneut die Änderung verlangt. Daher bietet sich an, etwas anderes, z. B. developer zu verwenden. ↩︎




Zuletzt geändert: 2025-12-11 21:11:48