plantpulse-server (Web-Konsole + IIoT-Engine)
Rolle
Dies ist das Kernmodul, das Gesicht und Gehirn der Plattform darstellt. Die Web-Konsole, die der Bediener sieht, die REST API, die von externen Systemen aufgerufen wird, und die IIoT-Engine, die die Industriedaten tatsächlich verarbeitet – all das läuft in diesem einen Modul.
| Element | Wert |
|---|---|
| Modulname | plantpulse-server |
| Container | plantpulse-server-web (eigenes Image, eigener Container) |
| Pfad im Container | /opt/kopens/plantpulse-platform/plantpulse-server/ |
| Benutzerzugriff | 80 / 443 — wird von plantpulse-proxy entgegengenommen und an diesen Container weitergegeben. Die Verwaltungskonsole läuft über dieselbe Adresse |
| Host-Port | Keiner — läuft ausschließlich hinter einem Proxy |
| Speicherlimit | DOCKER_SERVER_MEMORY Standard 14g / Heap DOCKER_SERVER_HEAP Standard 8g |
| Laufzeit | Apache Tomcat 10.1 + Java 21 |
| Durchsatz | 40.000 Nachrichten pro Sekunde (36-Thread-Pipeline) |
Der Benutzerverkehr läuft immer über plantpulse-proxy (80/443). Dadurch müssen bei einer Verschiebung oder Umbenennung des Backends weder Benutzer- noch Anlagenkonfigurationen angepasst werden. Der Containerstatus wird über bin/status.sh, Logs über bin/logs.sh plantpulse-server-web eingesehen.
Verantwortungsbereiche
Verzeichnisstruktur
plantpulse-server/
├── config/ # 서버 관리 설정 (외부화 — 배포해도 유지)
│ ├── plantpulse-engine.properties # IIoT 엔진 튜닝
│ ├── plantpulse-storage.properties # DB 연결
│ ├── plantpulse-mq.properties # 메시징 연결
│ └── log4j2.xml # 로깅 설정 (자가 시딩 — 최초 기동 시 생성)
├── bin/
│ ├── start.sh
│ ├── stop.sh
│ └── log-viewer.sh
├── logs/
│ └── system.log
├── path/ # 경로 매핑
└── server/ # 내장 Tomcat
├── conf/
│ ├── server.xml # 커넥터 / 포트
│ ├── context.xml # 컨텍스트 / 데이터소스
│ ├── catalina.properties
│ ├── logging.properties
│ ├── tomcat-users.xml
│ ├── web.xml
│ └── Catalina/localhost/
│ └── ROOT.xml # 컨텍스트 설정 (sessionCookieName 등)
└── webapps/
├── ROOT.war # 웹 애플리케이션 (WAR 통배포)
└── ROOT/ # Tomcat 이 unpack 한 디렉토리 (수정 금지)
Bereitstellungsmethode: Die Web-App wird per
server/webapps/ROOT.war-Austausch bereitgestellt (WAR-Gesamtbereitstellung, Umstellung läuft). Die bisherigeapp/plantpulse-server-webexploded-Verzeichnismethode wird eingestellt; dieserver.xmlContext-docBase-Overrides wurden entfernt. Wenn Dateien direkt inROOT/geändert werden, gehen sie bei der nächsten Bereitstellung verloren. Konfigurationsänderungen sollten daher immer überconfig/vorgenommen werden.
Wichtige Konfigurationsdateien
Die Konfigurationsdateien liegen im Verzeichnis config/ des Moduls. Der Server sucht sie in der Reihenfolge: -Dpp.conf.dir → ${catalina.base}/../config → Klassenpfad (Fallback). Wenn eine externe Konfiguration geladen wird, wird beim Start Properties loaded from external conf: ausgegeben.
application.properties wurde entfernt (06/2026). Konsoleinstellungen wie Theme / Startseite werden in der Konsole unter System > Einstellungsverwaltung (
mm_config-Tabelle) verwaltet;alarm.duplicate.check.minuteswurde nachplantpulse-engine.propertiesverlagert.
plantpulse-engine.properties
Tuning der IIoT-Engine-Pipeline.
| Element | Standardwert | Beschreibung |
|---|---|---|
engine.pipeline.threads | 36 | Anzahl paralleler Verarbeitungsthreads |
engine.pipeline.ratelimit | 40000 | Maximale Nachrichten pro Sekunde |
engine.pipeline.batch.size | 1000 | Batchgröße |
engine.pipeline.queue.size | 1200000 | Warteschlangenkapazität |
engine.dedup.enabled | true | Deduplizierung aktivieren |
plantpulse-storage.properties
Verbindung zu Datenspeichern.
# Cassandra
cassandra.host=${PP_CASSANDRA_HOST}
cassandra.port=${PP_CASSANDRA_PORT}
cassandra.keyspace=${PP_KEYSPACE}
cassandra.user=${PP_CASSANDRA_USER}
cassandra.password=${PP_CASSANDRA_PASSWORD}
# PostgreSQL (Spring DataSource)
postgres.url=jdbc:postgresql://${PP_POSTGRES_HOST}:${PP_POSTGRES_PORT}/${PP_DB_NAME}
postgres.user=${PP_PG_USER}
postgres.password=${PP_PG_PASSWORD}
# Valkey
redis.host=${PP_REDIS_HOST}
redis.port=${PP_REDIS_PORT}
redis.password=${PP_REDIS_PASSWORD}
Diese App rendert keine Konfiguration. Nach der Aufteilung in einen Container startet der Webserver ohne den Konfigurationsrenderer des Data Lake und liest Werte aus drei Quellen: ① vom Bediener platzierte externe Datei → ② im WAR eingebettete Standardwerte → ③ vom Compose übergebene Umgebungsvariablen. Von diesen überschreiben die Umgebungsvariablen die eingebetteten Standardwerte.
Die alleinige Definitionsquelle für die von der App gesehenen Werte ist daher
compose/docker-compose.yml. Namen, die nur im Data Lake existieren und nicht im Compose, erreichen diese App nicht — genau das war der Vorfall vom 29.08.2026, bei dem fünf App-Instanzen alte Namen weiterverwendeten, obwohl der Data Lake die Bezeichner geändert hatte.Ändern Sie keine Dateien im Inneren der Web-App (
server/webapps/ROOT/) – sie gehen bei der Bereitstellung verloren.
server/conf/server.xml
Tomcat-Connector-Einstellungen. Portänderung / TLS / Kompression / Thread-Pool.
<Connector port="80" protocol="HTTP/1.1"
connectionTimeout="20000"
maxThreads="500"
acceptCount="200"
URIEncoding="UTF-8"
compression="on"
compressibleMimeType="text/html,text/css,application/json,application/javascript" />
<Connector port="443" protocol="org.apache.coyote.http11.Http11Nio2Protocol"
SSLEnabled="true"
maxThreads="500"
sslEnabledProtocols="TLSv1.3,TLSv1.2">
<SSLHostConfig>
<Certificate certificateKeystoreFile="/var/security/plantpulse/server.jks"
certificateKeystorePassword="${PP_TLS_KEYSTORE_PASSWORD}" />
</SSLHostConfig>
</Connector>
Betriebsbefehle
Auf dem Host — pro Container
Den Container neu zu starten, ist gleichbedeutend mit einem Neustart dieses Moduls. Infrastruktur und andere Apps sind davon nicht betroffen.
cd /opt/kopens/plantpulse-platform-docker
# 서버만 재시작
docker compose -f compose/docker-compose.yml restart plantpulse-server-web
# 상태 · 로그
bin/status.sh
bin/logs.sh plantpulse-server-web -n 200
In den Container wechseln
cd /opt/kopens/plantpulse-platform-docker/bin
./shell.sh plantpulse-server-web
restart-server.sh des Data Lake funktioniert nichtDieses Skript ist nur ein dünner Delegat, das den Launcher des jeweiligen Moduls innerhalb des eigenen Containers aufruft. Im Data-Lake-Container gibt es keine Webserver-Executables, daher endet es mit einem Fehler «Modul nicht vorhanden». Verwenden Sie bitte den oben beschriebenen Neustart auf Containerebene.
Hot Reload (Konfiguration ohne Neustart übernehmen)
Einige Einstellungen lassen sich im Menü System > Einstellungsverwaltung der Konsole hot-reloaden. config/-Property-Dateien oder server.xml und andere Startzeit-Einstellungen erfordern einen Neustart.
Logs
| Log | Pfad | Inhalt |
|---|---|---|
| Hauptlog | logs/system.log | Anwendungslogik, Fehler |
| Tomcat catalina | server/logs/catalina.out | Tomcat-Standardausgabe |
| Zugriffslog | server/logs/localhost_access_log.YYYY-MM-DD.txt | HTTP-Request-Log |
| Engine-Log | logs/engine.log | Details zur IIoT-Engine-Verarbeitung |
| Slow Query | logs/slow.log | Abfragen über 200 ms |
Log-Level ändern — Die Logging-Konfiguration (log4j2.xml) ist als Modul-config/ externalisiert (Self-Seeding: Beim ersten Start werden die WAR-Standardwerte nach config/log4j2.xml kopiert; bei Anwendung wird Logging reconfigured from external conf: im Startlog ausgegeben):
# 웹 서버 컨테이너 안에서
cd /opt/kopens/plantpulse-platform-docker/bin
./shell.sh plantpulse-server-web
vi /opt/kopens/plantpulse-platform/plantpulse-server/config/log4j2.xml
# <Logger name="plantpulse" level="DEBUG"/> 등 수정 후 exit
# 호스트에서 그 컨테이너만 재시작
cd /opt/kopens/plantpulse-platform-docker
docker compose -f compose/docker-compose.yml restart plantpulse-server-web
Da es sich um eine externe Datei handelt, bleibt sie auch bei einer WAR-Neubereitstellung erhalten. Ändern Sie nichts im Inneren der Web-App (
server/webapps/ROOT/WEB-INF/classes/log4j2.xml) – es geht bei der Bereitstellung verloren.
Performance-Tuning
JVM-Heap
# server/bin/setenv.sh (없으면 생성)
export CATALINA_OPTS="-Xms16g -Xmx32g \
-XX:+UseG1GC -XX:MaxGCPauseMillis=200 \
-XX:+HeapDumpOnOutOfMemoryError \
-XX:HeapDumpPath=/var/log/plantpulse/heap/ \
-Xlog:gc*:file=/var/log/plantpulse/gc.log:time,uptime:filecount=10,filesize=100M"
Tomcat-Thread-Pool
In server.xml' Connector maxThreads / acceptCount anpassen. Bei hoher gleichzeitiger Last werden folgende Werte empfohlen.
| Gleichzeitige Verbindungen | maxThreads | acceptCount |
|---|---|---|
| ~ 100 | 200 | 100 |
| ~ 500 | 500 | 200 |
| 500+ | 1000 | 500 |
IIoT-Engine-Durchsatz
engine.pipeline.threads in plantpulse-engine.properties auf 70–80 % der Host-Kernanzahl einstellen. Bei Nachherstau auch engine.pipeline.queue.capacity entsprechend erhöhen.
Ausführliches Tuning finden Sie auf der Seite Performance-Tuning.
Wo landen Uploads? – RustFS
Anlagendokumente · Anlagenbilder · Profilbilder werden im Object Store (RustFS) gespeichert. In der Datenbank wird anstelle des Inhalts nur s3://<버킷>/<key> abgelegt.
| Element | Wert |
|---|---|
| Bucket | PP_S3_UPLOAD_BUCKET (Standard upload) — unterscheidet sich vom plantpulse-Bucket des Data Lake |
| Zugriff | PP_S3_HOST · PP_S3_PORT · PP_S3_USER · PP_S3_PASSWORD |
| Ablageort | Nur in plantpulse-server-web. Nicht auf Worker-Knoten |
Der Grund für die Bucket-Trennung ist, dass Aufbewahrungsregeln der einen Seite nicht die andere beeinflussen.
2026.09 erstellt den Bucket beim Start nicht. Wenn auf einen nicht existierenden Bucket verwiesen wird, schlagen Uploads fehl. Bei einer Neuinstallation wird der Bucket während der Migration angelegt; wenn Sie den Wert manuell ändern und auf einen anderen Bucket verweisen, müssen Sie diesen Bucket zuerst erstellen.
Wenn die Verbindungswerte leer sind, kann der Object Store nicht erstellt werden und der Upload-Pfad erscheint nicht. Die Container-Umgebung ist zum Zeitpunkt der Erstellung festgelegt. Wenn Sie Werte hinzufügen, muss der Container neu erstellt werden, nicht nur docker restart.
Upload-Limits — nicht in einer Zeile darstellbar
Die Limits liegen auf zwei Ebenen, und das tatsächlich zuerst wirksame ist fast immer die untere (Endpunkt).
Endpunkt-Guards — greifen zuerst
| Hochgeladenes | Endpunkt | Limit |
|---|---|---|
| Anlagendokumente | POST /asset/tree/document/upload | 50 MiB |
| Anlagendokumente (API v5) | POST /api/v5/assets/{asset_id}/documents | 50 MiB |
| Anlagenbilder | POST /asset/image/upload | 10 MiB |
| Canvas SVG | POST /canvas/svgupload | 10 MiB |
Anlagenbilder und Canvas-SVG werden bei 10 MiB abgelehnt. Nur Dokumente haben 50 MiB.
Was bei Überschreitung angezeigt wird
Alle vier Endpunkte verwenden dieselbe Validierungsfunktion, und die Meldung enthält nur die Byte-Anzahl.
file_size: 12582912 > max 10485760
Beispiel beim Hochladen eines 12-MiB-Bilds: 10485760 ist 10 MiB, 52428800 ist 50 MiB.
Die Meldung enthält keine Einheit, sodass der Bediener Bytes manuell umrechnen muss, und es wird auch nicht angezeigt, welche Datei betroffen ist. Wenn mehrere Dateien gleichzeitig hochgeladen und abgelehnt werden, muss die Größe manuell geprüft werden.
Container-Limit — die Obergrenze darüber
| Was | Wert | Wo |
|---|---|---|
| Einzelne Datei | 50 MiB | web.xml' <max-file-size> |
| Gesamte Multipart-Anfrage | 52 MiB | web.xml' <max-request-size> |
| Formularparameter (außer Datei-Parts) | 2 MiB | Tomcat-Standardwert — beide Connector deklarieren kein maxPostSize |
maxPostSize ist das Limit für Bytes, die Tomcat in Request-Parameter umwandelt. x-www-form-urlencoded zählt den gesamten Body, multipart/form-data zählt nur die Nicht-Datei-Teile. Raw-Bodys wie JSON sind nicht betroffen.
Messung (18.09.2026): Wird ein 3-MiB-Multipart über den Proxy gesendet, wird der gesamte Body übertragen und erreicht die Anwendungs-Authentifizierungsschicht (endet nicht wegen der Größe, sondern wegen 401). Es gibt keine 2-MiB-Wand im Übertragungspfad; nginx ist client_max_body_size 200m.
Vor dem 18.09.2026 hatte nur der 443-Connector ein maxPostSize="-1", sodass derselbe Server je nach Port unterschiedliche Limits erzwang. Jetzt sind 80 und 443 gleich.
Health Check
# 표준 헬스체크 (익명, readiness — 엔진이 전 단계 기동을 완료(RUNNING)했을 때만 UP. 배포 자동검증 / 모니터링용)
curl -fsS http://127.0.0.1/api/health
# 준비 완료: 200 {"status":"UP","service":"plantpulse-server-web","ts":1765500000000,"checks":{"engine":"RUNNING"}}
# 기동 중: 503 {"status":"STARTING",...} / 기동 실패·중지: 503 {"status":"DEGRADED",...}
# 경로는 /api/health 다 — «/api/v5/health» 는 없다. 인증도 필요 없다(익명 허용 경로).
# 콘솔 ping
curl -fsS http://127.0.0.1/api/v5/ping
# 공개 상태 페이지의 데이터 (익명 — 24시간 가용성·p99·엔드포인트별 상태)
curl -fsS http://127.0.0.1/api/v5/public/status
# WebSocket 핸드셰이크 확인
curl -i -N \
-H "Connection: Upgrade" -H "Upgrade: websocket" \
-H "Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==" \
-H "Sec-WebSocket-Version: 13" \
http://127.0.0.1:8000/ws
Häufig auftretende Probleme
| Symptom | Ursache | Maßnahme |
|---|---|---|
OutOfMemoryError | Heap zu klein | -Xmx in setenv.sh erhöhen + GC-Log analysieren |
Too many open files | Dateideskriptor-Limit | nofile in /etc/security/limits.conf auf 65535+ setzen |
| Langsame Konsolenantwort | Langsame DB-Abfragen | PostgreSQL-Slow-Query-Log prüfen, Valkey-Cache-Trefferquote prüfen |
| Endlose Login-Schleife | Sitzungs- / Cookie-Problem | Browser-Cache löschen, sessionCookieName in server/conf/Catalina/localhost/ROOT.xml prüfen |
| 502 / 504 (Reverse Proxy) | Backend-Antwortzeit überschritten | nginx proxy_read_timeout erhöhen |
| WebSocket-Abbrüche | Firewall-Idle-Timeout | Firewall-Timeout > 60 s, tomcat.websocket.session.timeout anpassen |
Detaillierte Diagnose finden Sie auf der Seite Problemlösung.
Sicherheits-Checkliste
- Standard-Admin-Passwort ändern (
admin / admin123!) - Manager-Konto in
tomcat-users.xmlentfernen oder starkes Passwort setzen -
shutdown port (7000)inserver.xmlvor externem Zugriff schützen - HTTPS-Zertifikat anwenden (von
plantpulse-certseingebettetespp-security-Volumen-Material oder externe CA) - X-Frame-Options / CSP / HSTS-Header-Einstellungen prüfen
- API-Bearer-Token im Betriebs-Vault speichern
- Debug-Optionen in
config/-Property-Dateien deaktivieren und Rechte einschränken (chmod 600)
Ausführliche Sicherheitseinstellungen finden Sie auf der Seite Sicherheitseinstellungen.