eeab1ddbe5
Das docker-compose.yml des Haupt-Stacks deklariert kein Netz - Compose legt <projektname>_default selbst an. Bei Portainer ist der Projektname der Stack-Name, der in readarchive-stack.yml eingetragene Wert noisesensors_default passt also nur, wenn das Stack auch so heisst. Sonst bricht der Lauf mit "declared as external, but could not be found" ab. Dazu als Option das Umbenennen des Default-Netzes (fester Name, unabhaengig vom Stack-Namen) und der Hinweis, dass ein networks: an einem Service dessen automatische Zuordnung aufhebt - wer dort etwas eintraegt, muss default mit auffuehren. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
231 lines
12 KiB
Markdown
231 lines
12 KiB
Markdown
# Projekt Lärm-Sensoren
|
|
|
|
## Installation der Container
|
|
### Dateien
|
|
#### Directory-Struktur
|
|
Die Datei **noise.tgz** enthält die folgenden Dateien und Verzeichnisse:
|
|
|
|
~~~
|
|
<LOCALDIR>
|
|
|
|
|
+- <d>noisesensors
|
|
|
|
|
+- <f>docker-compose.yml
|
|
+- <f>readarchive-stack.yml
|
|
+- <d>log
|
|
+- <d>data
|
|
| +- <d>mongo
|
|
| | +- <d>data
|
|
| | +- <d>entries
|
|
| | | +- <f>create.js
|
|
| + <d>influx
|
|
| | + <d>data
|
|
|
|
<f>: file, <d>: directory
|
|
~~~
|
|
|
|
* **docker-compose.yml**
|
|
enthält die Anweisungen für die Docker-Container. Dies kann so an das aktuelle Portainer-Stack angehängt werden.
|
|
* **readarchive-stack.yml**
|
|
eigenes, kleines Stack für den Einmal-Lauf *readarchive* (siehe unten). Gehört bewusst nicht ins Haupt-Stack, weil es kein Dauerdienst ist.
|
|
* **log**
|
|
Ablage für die Logdateien der einzelnene Container (**noise.log**, **sensorapi.log** und **readin.log**) . Diese werden täglich komprimiert. Es werden Versionen für 7 Tage aufgehoben.
|
|
* **data**
|
|
hier darunter befinden sich alle Daten, sowohl die Datenbanken als auch Konfigurationen. Außerdem werden jeweils die laufend eingelesenen Daten als **actdata.json** abgelegt. Diese werden bei jedem neuen Einlesen wieder überschrieben.
|
|
* **data/mongo**
|
|
Verzeichnis für die Mongo-Datenbank
|
|
* **data/mngo/data**
|
|
hier befinden sich die eingelesenen Daten
|
|
* **data/mongo/entries**
|
|
hier liegt die Datei **create.js**, mit deren Hilfe bei ersten Start der Datenbank die Collections und die Indexe erzeugt werden.
|
|
* **data/influx**
|
|
Verzeichnis für die Influx-Datenbank
|
|
* **data/influx/data**
|
|
Hier dann die eingelesenen Daten
|
|
|
|
|
|
### Aufrufe
|
|
Entpacken des Tar-Files **noise.tgz** in einem beliebigen Verzeichnis (das Direktoty *noisesensors* wird angelegt). Der komplette Pfad zu dem Verzeichnis (also ab root) muss in der Environmentvariablen **LOCALDIR** hinterlegt werden.
|
|
|
|
Nun das *docker-compose.yml* in das Portainer-Stack einfügen. Zusätzlich folgende Environment-Variablen festlegen:
|
|
|
|
~~~
|
|
DOCKER_INFLUXDB_INIT_USERNAME=<username>
|
|
DOCKER_INFLUXDB_INIT_PASSWORD=<passwort>
|
|
DOCKER_INFLUXTOKEN=<token>
|
|
MONGO_ROOT_USERNAME=<username1>
|
|
MONGO_ROOT_PASSWORD=<passwort1>
|
|
LOCALDIR=<directory, in die das .tgz entpackt wurde>
|
|
STORE=influx
|
|
~~~
|
|
\<username>, \<passwort>, \<username1>, \<passwort1> und \<token> sind anzupassen.
|
|
|
|
Danach einloggen in das Docker-Registry auf *citysensor.de*:
|
|
|
|
~~~
|
|
docker login docker.citysensor.de
|
|
~~~
|
|
Einloggen mit Username und Passwort (siehe mail), anschließend kann das Portainer-Stack gestartet werden (die Container werden beim ersten mal automatisch von *docker.citysensor.de* herunter geholt).
|
|
|
|
Damit sollte dann Alles laufen. Das Programm holt alle 5min die neuesten Daten von *sensor.community* ab und speichert die Messwerte in der InfluxDB und die Metadaten des Sensors in der Mongo-Datenbank.
|
|
Das Logfile kann via **tail -f log/noise.log** im Verzeichnise **noisesensors** mitgeschrieben werden.
|
|
|
|
|
|
### Container
|
|
* **readin**
|
|
Über diesen Container werden alle 5 min. die aktuellen Daten von *sensor.community* abgeholt und in den beiden Datenbanken gespeichert
|
|
|
|
* **readarchive**
|
|
Gegenstück zu *readin*: liest rückwirkend die Tagesdateien von *archive.sensor.community* ein, um Lücken zu füllen. Kein Dauerdienst, sondern ein Lauf mit Parametern — deshalb steht er **nicht** im Haupt-Stack, sondern in der eigenen Datei `readarchive-stack.yml`.
|
|
|
|
Auf der Kommandozeile mit Argumenten:
|
|
~~~
|
|
docker compose -f readarchive-stack.yml run --rm readarchive \
|
|
-t noise -s 2026-07-22 -e 2026-07-23 -d influx
|
|
~~~
|
|
|
|
Im **Portainer** als eigenes Stack anlegen (Inhalt von `readarchive-stack.yml`) und die Parameter unten als *Environment variables* eintragen — `docker compose run` hat dort kein Gegenstück, aber `readFromcsv.js` liest jeden Parameter auch aus einer Env-Variablen:
|
|
|
|
CLI | Env | Default
|
|
---- | ---- | ----
|
|
`-s` | `START` | gestern
|
|
`-e` | `END` | Startdatum + 1 Tag (**exklusiv**)
|
|
`-t` | `TYP` | `noise`
|
|
`-d` | `DBASE` | `both`, in der Stack-Datei auf `influx` vorbelegt
|
|
|
|
*Deploy the stack* startet den Lauf, der Container endet danach und bleibt als „Exited" stehen. Für den nächsten Zeitraum nur die Variablen ändern und *Update the stack*. Der Haupt-Stack wird dabei nicht angefasst.
|
|
|
|
Beide Wege brauchen dieselben Zugangsdaten und `LOCALDIR` — auf der Kommandozeile aus einer `.env` neben der Datei, im Portainer aus den Stack-Variablen. Auch mit `-d influx` wird Mongo gebraucht, dort liegen die Metadaten der Sensoren.
|
|
|
|
**Der Netz-Name ist die Stolperstelle.** Das `docker-compose.yml` des Haupt-Stacks deklariert kein Netz — Compose legt für jedes Projekt automatisch `<projektname>_default` an und hängt alle Services hinein; daher funktioniert `MONGOHOST: mongodb`. Bei Portainer ist der Projektname **der Stack-Name**. Heißt das Stack dort `noisesensors`, passt der in `readarchive-stack.yml` eingetragene Wert `noisesensors_default`; heißt es `laerm`, muss dort `laerm_default` stehen, sonst bricht der Lauf ab:
|
|
|
|
~~~
|
|
network noisesensors_default declared as external, but could not be found
|
|
~~~
|
|
|
|
Vor dem ersten Lauf also im Portainer unter *Networks* nachsehen (oder `docker network ls`) und den Namen in `readarchive-stack.yml` angleichen. Das Netz wird nur an einer Stelle erzeugt — im Haupt-Stack implizit. Es auch dort als `external` zu deklarieren, führt dazu, dass es niemand mehr anlegt.
|
|
|
|
**Option: fester Netz-Name.** Wer die Abhängigkeit vom Stack-Namen loswerden will, benennt im Haupt-Stack das Default-Netz um:
|
|
|
|
~~~yaml
|
|
networks:
|
|
default:
|
|
name: noisesensors_net
|
|
~~~
|
|
|
|
Kein Service braucht dafür einen eigenen `networks:`-Eintrag — die automatische Zuordnung bleibt, das Netz heißt nur fest `noisesensors_net`, und in `readarchive-stack.yml` steht dieser Name statt `<stack-name>_default`. Der Preis: Beim Umstellen wechseln alle Container das Netz und werden neu erzeugt, Mongo und Influx sind also kurz weg. Die Daten liegen auf Bind-Mounts und bleiben unangetastet — trotzdem gehört das in ein Wartungsfenster und nicht nebenbei erledigt.
|
|
|
|
Umgekehrt gilt: Ein `networks:` an einem Service hebt dessen automatische Zuordnung auf. Wer dort etwas einträgt — etwa um *noise* zusätzlich ins Traefik-Netz zu hängen —, muss `default` mit aufführen, sonst erreicht der Service die übrigen Container des Stacks nicht mehr.
|
|
|
|
Ein Tag dauert etwa zwei Minuten. Schlägt ein Influx-Write fehl, werden die betroffenen Sensoren am Ende aufgelistet und der Exit-Code ist ≠ 0 (im Portainer als „Exited (1)" sichtbar).
|
|
|
|
* **logrotate**
|
|
Mit Hilfe dieses Containers werde die Log-Files im Unterverzeichnis *log* täglich rotiert. Es werden 7 Versionen behalten, ältere gelöscht.
|
|
|
|
* **sensorapi**
|
|
Über diesen Container werde die Daten aus der Mongo-Datenbank ausgelesen, aufbereitet und als JSON- oder CSV-Datei ausgegeben.
|
|
|
|
* **noise**
|
|
Mit diesem Container wird die Grafik aufbereitet. Auch kann darüber die API (im Container *sensorapi*) angesprochen und die Daten abgerufen werden. (Für die Parameter der Anfrage siehe die extra Beschreibung **Laerm_API**). Dieser Container sollte von außen über einen Domainnamen erreichbar sein. Ansonsten hört er auf **Port 3003** auf HTTP-Anfragen.
|
|
|
|
* **mongodb**
|
|
Die Mongo-Datenbank. Es gibt eine Datenbank namens **sensor_data**. Diese hat folgende Collections:
|
|
* properties
|
|
Hier liegen für alle Sensoren die Metadaten (wie Ortskoordinaten, Typ, Zeitpunkt der letzten Übertragung etc.)
|
|
* statistics
|
|
Dient nur Kontrolle, schreibt verschiedenen Statistikdaten wie Dauer des Einlesens, Dauer des Speicherns in den DBs etc.
|
|
**Zugriff:**
|
|
Am Besten über das Programm **MongoDB Compass**. Zugangsdaten entprechend der im Portainer hinterlegten.
|
|
|
|
* **influxdb**
|
|
Die Influx-Datenbank. Hier werden in dem Bucket *sensor_data* die reinen Messwert der einzelnen Sensoren gespeichert.
|
|
**Zugriff:**
|
|
http://\<server>:8086
|
|
Logindaten: Entsprechen der im Portainer hinterlegten.
|
|
|
|
|
|
## Sourcen für die Container
|
|
|
|
Das gesamte Projekt ist im GitHub unter **laermsensor-stack** abgelegt. Für jeden der 4 Container existiert darunter ein Verzeichnis:
|
|
**readin**, **readarchive**, **sensorapi** und **noise**. In diesen Verzeichnissen sind alle benötigten Sourcen enthalten.
|
|
|
|
Daneben gibt es **common** mit den Modulen, die sich *readin* und *readarchive* teilen: `mongo.js`, `influx_post.js`, `logit.js` und `statistics.js`. Sie lagen früher in beiden Komponenten doppelt und sind auseinandergelaufen. Wer dort etwas ändert, ändert es für beide — nach einer Änderung also **beide** Container neu bauen.
|
|
|
|
Sollte was geändert werden, so muss mit **deploy.sh** der Container neu erzeugt und auf die Registry (siehe oben) gepushed werden. Der Build-Kontext von *readin* und *readarchive* ist wegen `common/` das Wurzelverzeichnis des Repositories; `deploy.sh` wechselt selbst dorthin und kann aus dem Komponentenverzeichnis aufgerufen werden.
|
|
|
|
`deploy.sh` nimmt optional ein Tag als Argument:
|
|
~~~
|
|
./deploy.sh # nur :latest
|
|
./deploy.sh 2026-08-01 # :2026-08-01 und zusätzlich :latest
|
|
~~~
|
|
**Ein Tag mitgeben.** Wird nur `latest` gepusht, überschreibt jeder Build den vorherigen Stand und es gibt keinen Weg zurück (siehe *Update eines laufenden Stacks*).
|
|
|
|
Das Verzeichnis **noisesensors** ist nur dummy, es spiegelt die Dateistruktur für das aktuelle Stack ab, wie oben beschrieben (wird mit Hilfe von **noise.tgz** erzeugt).
|
|
|
|
## Update eines laufenden Stacks
|
|
|
|
Betrifft eine Installation, die bereits läuft und auf einen neueren Stand gebracht werden soll.
|
|
|
|
### 1. Rollback-Möglichkeit sicherstellen
|
|
|
|
**Vor allem anderen.** Zeigt in der Registry nur `latest` auf das Image, existiert der laufende Stand nach dem Pull nirgends mehr. Prüfen, welche Tags es gibt:
|
|
|
|
~~~
|
|
curl -H "Authorization: Basic <auth aus ~/.docker/config.json>" \
|
|
https://docker.citysensor.de/v2/readin/tags/list
|
|
~~~
|
|
|
|
Fehlt ein benanntes Tag für den laufenden Stand, ihn zuerst benennen und pushen. Das alte Image findet sich auf dem Build-Rechner meist noch unbenannt (`docker images | grep readin`):
|
|
|
|
~~~
|
|
docker tag <alte-image-id> docker.citysensor.de/readin:<datum-alt>
|
|
docker push docker.citysensor.de/readin:<datum-alt>
|
|
~~~
|
|
|
|
### 2. docker-compose.yml im Portainer angleichen
|
|
|
|
Den Stand aus `noisesensors/docker-compose.yml` dieses Repositories übernehmen. Zu beachten:
|
|
|
|
* **Zwingend** bei einem Update von einem Stand vor August 2026: das Volume von *readin* muss auf `/opt/app/readin/data` zeigen, nicht mehr auf `/opt/app/data`. Das Layout im Image spiegelt seit der Einführung von `common/` das Repository. Bleibt der alte Pfad stehen, schreibt readin `aktdata.json` in die Container-Schicht statt auf den Host.
|
|
* Der Service **readarchive** gehört nicht mehr ins `docker-compose.yml`. Steht er dort noch (mit `profiles: ["tools"]`), kann er beim Update entfallen — er wird durch das eigene Stack `readarchive-stack.yml` ersetzt.
|
|
* **Nicht** aufnehmen: ein Mount auf `/etc/influxdb2`. Das Entrypoint von influxdb legt dort beim Setup das CLI-Profil `default` an. Ist bereits eines vorhanden, scheitert der Setup — und das Entrypoint löscht daraufhin `influxd.bolt` und `engine/` im Datenverzeichnis und startet neu. Weil der Mount das Aufräumen überlebt, wiederholt sich das endlos und der Datenbestand ist bei jeder Runde weg.
|
|
|
|
### 3. Nur den geänderten Container ziehen
|
|
|
|
Nicht den ganzen Stack neu starten — Mongo und Influx bleiben unangetastet:
|
|
|
|
~~~
|
|
docker compose pull readin
|
|
docker compose up -d readin
|
|
~~~
|
|
|
|
Im Portainer entspricht das dem Aktualisieren des Stacks mit *Re-pull image and redeploy*.
|
|
|
|
### 4. Verifizieren
|
|
|
|
Der nächste Lauf kommt binnen fünf Minuten:
|
|
|
|
~~~
|
|
tail -20 <LOCALDIR>/noisesensors/log/readin.log
|
|
ls -l <LOCALDIR>/noisesensors/data/aktdata.json
|
|
~~~
|
|
|
|
Erwartet werden `Influx-Write-Time` und `Program end` sowie ein frischer Zeitstempel an `aktdata.json` — letzterer belegt, dass der Volume-Pfad stimmt.
|
|
|
|
### 5. Rollback
|
|
|
|
Image-Tag im Stack auf den in Schritt 1 gesicherten Stand setzen, die Volume-Zeile auf den alten Pfad zurückdrehen, redeploy.
|
|
|
|
## Versionen
|
|
|
|
Version | Datum | Bemerkung
|
|
---- | ---- | ----
|
|
1.0.0 | 2023-11-30 |
|
|
2.0.0 | 2026-07-25 | Anpassung an Stack-Änderungen
|
|
2.1.0 | 2026-08-01 | readarchive dazu, gemeinsame Module unter common/, Zeitstempel in UTC
|
|
2.1.1 | 2026-08-02 | readarchive als eigenes Stack (readarchive-stack.yml)
|
|
|
|
|
|
|
|
|