Zum Hauptinhalt springen

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.

ElementWert
Modulnameplantpulse-server
Containerplantpulse-server-web (eigenes Image, eigener Container)
Pfad im Container/opt/kopens/plantpulse-platform/plantpulse-server/
Benutzerzugriff80 / 443 — wird von plantpulse-proxy entgegengenommen und an diesen Container weitergegeben. Die Verwaltungskonsole läuft über dieselbe Adresse
Host-PortKeiner — läuft ausschließlich hinter einem Proxy
SpeicherlimitDOCKER_SERVER_MEMORY Standard 14g / Heap DOCKER_SERVER_HEAP Standard 8g
LaufzeitApache Tomcat 10.1 + Java 21
Durchsatz40.000 Nachrichten pro Sekunde (36-Thread-Pipeline)
Dieser Container öffnet keinen Host-Port

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 bisherige app/plantpulse-server-web exploded-Verzeichnismethode wird eingestellt; die server.xml Context-docBase-Overrides wurden entfernt. Wenn Dateien direkt in ROOT/ geändert werden, gehen sie bei der nächsten Bereitstellung verloren. Konfigurationsänderungen sollten daher immer über config/ 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.minutes wurde nach plantpulse-engine.properties verlagert.

plantpulse-engine.properties​

Tuning der IIoT-Engine-Pipeline.

ElementStandardwertBeschreibung
engine.pipeline.threads36Anzahl paralleler Verarbeitungsthreads
engine.pipeline.ratelimit40000Maximale Nachrichten pro Sekunde
engine.pipeline.batch.size1000Batchgröße
engine.pipeline.queue.size1200000Warteschlangenkapazität
engine.dedup.enabledtrueDeduplizierung 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
Ein Neustart über restart-server.sh des Data Lake funktioniert nicht

Dieses 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​

LogPfadInhalt
Hauptloglogs/system.logAnwendungslogik, Fehler
Tomcat catalinaserver/logs/catalina.outTomcat-Standardausgabe
Zugriffslogserver/logs/localhost_access_log.YYYY-MM-DD.txtHTTP-Request-Log
Engine-Loglogs/engine.logDetails zur IIoT-Engine-Verarbeitung
Slow Querylogs/slow.logAbfragen ü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 VerbindungenmaxThreadsacceptCount
~ 100200100
~ 500500200
500+1000500

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.

ElementWert
BucketPP_S3_UPLOAD_BUCKET (Standard upload) — unterscheidet sich vom plantpulse-Bucket des Data Lake
ZugriffPP_S3_HOST · PP_S3_PORT · PP_S3_USER · PP_S3_PASSWORD
AblageortNur 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.

Der Bucket muss vorab existieren

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.

Die Umgebung muss konfiguriert sein, um zu starten

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​

HochgeladenesEndpunktLimit
AnlagendokumentePOST /asset/tree/document/upload50 MiB
Anlagendokumente (API v5)POST /api/v5/assets/{asset_id}/documents50 MiB
AnlagenbilderPOST /asset/image/upload10 MiB
Canvas SVGPOST /canvas/svgupload10 MiB
Nur «Upload-Limit 50 MiB» zu kommunizieren, führt bei Bildern zu Fehlern

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.

Weder Einheit noch Dateiname

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​

WasWertWo
Einzelne Datei50 MiBweb.xml' <max-file-size>
Gesamte Multipart-Anfrage52 MiBweb.xml' <max-request-size>
Formularparameter (außer Datei-Parts)2 MiBTomcat-Standardwert — beide Connector deklarieren kein maxPostSize
2 MiB ist kein «Datei»-Limit — große Dateien werden auch über den Proxy hochgeladen

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​

SymptomUrsacheMaßnahme
OutOfMemoryErrorHeap zu klein-Xmx in setenv.sh erhöhen + GC-Log analysieren
Too many open filesDateideskriptor-Limitnofile in /etc/security/limits.conf auf 65535+ setzen
Langsame KonsolenantwortLangsame DB-AbfragenPostgreSQL-Slow-Query-Log prüfen, Valkey-Cache-Trefferquote prüfen
Endlose Login-SchleifeSitzungs- / Cookie-ProblemBrowser-Cache löschen, sessionCookieName in server/conf/Catalina/localhost/ROOT.xml prüfen
502 / 504 (Reverse Proxy)Backend-Antwortzeit überschrittennginx proxy_read_timeout erhöhen
WebSocket-AbbrücheFirewall-Idle-TimeoutFirewall-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.xml entfernen oder starkes Passwort setzen
  • shutdown port (7000) in server.xml vor externem Zugriff schützen
  • HTTPS-Zertifikat anwenden (von plantpulse-certs eingebettetes pp-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.

Zugehörige Dokumente​