plantpulse-server (ウェブコンソール + IIoTエンジン)
役割
プラットフォームの顔であり頭脳に相当する中核モジュールです。オペレーターが閲覧するウェブコンソール、外部システムが呼び出すREST API、そして産業データを実際に処理するIIoTエンジンが、すべてこの1つのモジュール内で動作します。
| 項目 | 値 |
|---|---|
| モジュール名 | plantpulse-server |
| コンテナ | plantpulse-server-web (自己イメージ、自己コンテナ) |
| コンテナ内パス | /opt/kopens/plantpulse-platform/plantpulse-server/ |
| ユーザー接続 | 80 / 443 — plantpulse-proxy が受け取ってこのコンテナへ渡します。管理コンソールも同じアドレスです |
| ホストポート | なし — プロキシの背後でのみ動作します |
| メモリ上限 | DOCKER_SERVER_MEMORY デフォルト 14g / ヒープ DOCKER_SERVER_HEAP デフォルト 8g |
| ランタイム | Apache Tomcat 10.1 + Java 21 |
| 処理量 | 毎秒40,000件メッセージ (36スレッドパイプライン) |
ユーザートラフィックは常に plantpulse-proxy(80/443) を経由します。そのため、バックエンドが移動したり名前が変わったりしても、ユーザー・設備設定を変更する必要がありません。コンテナの状態は bin/status.sh、ログは bin/logs.sh plantpulse-server-web で確認します。
責務領域
ディレクトリ構造
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 한 디렉토리 (수정 금지)
デプロイ方式: ウェブアプリは
server/webapps/ROOT.war差し替え方式でデプロイします (WAR一括デプロイ、移行中)。既存のapp/plantpulse-server-webexplodedディレクトリ方式は廃止予定であり、server.xmlの ContextdocBaseオーバーライドは削除されました。ROOT/内のファイルを直接修正すると次のデプロイ時に消えるため、設定変更は常にconfig/で行ってください。
主要設定ファイル
設定ファイルはモジュールの config/ ディレクトリにあり、サーバーは -Dpp.conf.dir → ${catalina.base}/../config → クラスパス(fallback) の順で探索します。外部設定がロードされると、起動ログに Properties loaded from external conf: が出力されます。
application.properties は削除されました (2026.06)。テーマ / ホームページなどコンソール動作設定はコンソールのシステム > 設定管理 (
mm_configテーブル) で管理し、alarm.duplicate.check.minutesはplantpulse-engine.propertiesに移管されました。
plantpulse-engine.properties
IIoTエンジンパイプラインのチューニング。
| 項目 | デフォルト値 | 説明 |
|---|---|---|
engine.pipeline.threads | 36 | 並列処理スレッド数 |
engine.pipeline.ratelimit | 40000 | 毎秒最大メッセージ数 |
engine.pipeline.batch.size | 1000 | バッチサイズ |
engine.pipeline.queue.size | 1200000 | キュー容量 |
engine.dedup.enabled | true | 重複除去の有効化 |
plantpulse-storage.properties
データストア接続。
# 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}
このアプリは設定をレンダリングしません。 コンテナに分かれた後、ウェブサーバーはデータレイクの設定レンダラーなしで起動し、値を3つの場所から読み取ります — ① オペレーターが置いた外部ファイル → ② WAR に内蔵されたデフォルト値 → ③ compose が渡す環境変数。この3つのうち、環境変数が内蔵デフォルト値を上書きします。
そのためアプリが見る値の定義元は
compose/docker-compose.ymlの1つだけです。データレイク側にのみ存在し compose に無い名前は、このアプリに届きません — データレイク側だけで識別子を変更したサイトで、アプリ5つが古い名前を使い続けた事故がそれです(2026-08-29)。ウェブアプリ内部(
server/webapps/ROOT/)のファイルは修正しないでください — デプロイ時に消えます。
server/conf/server.xml
Tomcatコネクタ設定。ポート変更 / TLS / 圧縮 / スレッドプール。
<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>
運用コマンド
ホストから — コンテナ単位
コンテナを再起動することが、すなわちこのモジュールの再起動です。インフラや他のアプリには影響しません。
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
コンテナ内に入る
cd /opt/kopens/plantpulse-platform-docker/bin
./shell.sh plantpulse-server-web
restart-server.sh では再起動されませんそのスクリプトは各アプリの自己コンテナ内でそのモジュールのランチャーを呼ぶ薄い委譲です。データレイクコンテナにはウェブサーバー実行ファイルが無いため、「モジュールが無い」というエラーで終了します。上記のコンテナ単位再起動を使用してください。
ホットリロード (再起動なしで設定反映)
一部の設定はコンソールのシステム > 設定管理メニューからホットリロードが可能です。config/ のプロパティファイルや server.xml など起動時設定は再起動が必要です。
ログ
| ログ | パス | 内容 |
|---|---|---|
| メインログ | logs/system.log | アプリケーションロジック、エラー |
| Tomcat catalina | server/logs/catalina.out | Tomcat標準出力 |
| アクセスログ | server/logs/localhost_access_log.YYYY-MM-DD.txt | HTTPリクエストログ |
| エンジンログ | logs/engine.log | IIoTエンジン処理詳細 |
| スロークエリ | logs/slow.log | 200ms以上のクエリ |
ログレベル変更 — ロギング設定(log4j2.xml)はモジュール config/ に外部化されています (自己シーディング: 初回起動時にWARデフォルト値が config/log4j2.xml にコピーされ、適用時に起動ログへ Logging reconfigured from external conf: 出力):
# 웹 서버 컨테이너 안에서
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
外部ファイルなのでWAR再デプロイでも永続的に保持されます。ウェブアプリ内部(
server/webapps/ROOT/WEB-INF/classes/log4j2.xml)は修正しないでください — デプロイ時に消えます。
パフォーマンスチューニング
JVMヒープ
# 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スレッドプール
server.xml の Connector で maxThreads / acceptCount を調整。大規模同時接続環境では以下を推奨します。
| 同時接続数 | maxThreads | acceptCount |
|---|---|---|
| ~ 100 | 200 | 100 |
| ~ 500 | 500 | 200 |
| 500+ | 1000 | 500 |
IIoTエンジン処理量
plantpulse-engine.properties の engine.pipeline.threads をホストコア数の70~80%に設定。メッセージ滞留時は engine.pipeline.queue.capacity も合わせて増やしてください。
詳細なチューニングはパフォーマンスチューニングページを参照してください。
アップロード物の保存先 — RustFS
設備文書 · 設備画像 · プロフィール画像はオブジェクトストア(RustFS) に保存されます。データベースには本体の代わりに s3://<버킷>/<key> が入ります。
| 項目 | 値 |
|---|---|
| バケット | PP_S3_UPLOAD_BUCKET (デフォルト upload) — データレイクが使用する plantpulse バケットとは異なります |
| 接続 | PP_S3_HOST · PP_S3_PORT · PP_S3_USER · PP_S3_PASSWORD |
| 配置先 | plantpulse-server-web のみに配置されます。ワーカーには行きません |
バケットを分けた理由は、片方の保存ルールがもう片方に影響しないようにするためです。
2026.09 は起動時にバケットを作成しません。 存在しないバケットを指すとアップロードが失敗します。クリーンインストールでは移行プロセスでバケットが作成されますが、値を手動で変更して別のバケットを指す場合は、そのバケットを先に作成しておいてください。
接続値が空の場合はオブジェクトストアを作成できず、アップロードパスが表示されません。コンテナ環境は作成時点で固定されるため、値を追加したら docker restart ではなくコンテナを再作成する必要があります。
アップロード上限 — 一行では書き表せません
上限は2層あり、実際に先に引っかかるのはほぼ常に下層(エンドポイント)です。
エンドポイント別ガード — 先に引っかかる
| アップロード対象 | エンドポイント | 上限 |
|---|---|---|
| 設備文書 | POST /asset/tree/document/upload | 50 MiB |
| 設備文書 (API v5) | POST /api/v5/assets/{asset_id}/documents | 50 MiB |
| 設備画像 | POST /asset/image/upload | 10 MiB |
| キャンバスSVG | POST /canvas/svgupload | 10 MiB |
設備画像とキャンバスSVGは10 MiBで拒否されます。文書のみ50 MiBです。
上限を超えた場合に表示されるもの
4つのエンドポイントは同じ検証関数を使用し、メッセージにはバイト数だけが表示されます。
file_size: 12582912 > max 10485760
12 MiBの画像をアップロードした場合の例です — 10485760 が10 MiB、52428800 が50 MiBです。
メッセージに単位がないため、オペレーターがバイト数を暗算する必要があり、どのファイルが引っかかったのかも表示されません。複数ファイルを一度にアップロードして拒否された場合、サイズを直接測って特定する必要があります。
コンテナ上限 — その上の天井
| 対象 | 値 | 設定場所 |
|---|---|---|
| 1ファイル | 50 MiB | web.xml の <max-file-size> |
| マルチパートリクエスト全体 | 52 MiB | web.xml の <max-request-size> |
| フォームパラメータ(ファイルパートを除く) | 2 MiB | Tomcatデフォルト値 — 両コネクタとも maxPostSize を宣言していません |
maxPostSize はTomcatがリクエストパラメータに変換するバイトの上限です。x-www-form-urlencoded はボディ全体を、multipart/form-data はファイル以外のパートのみをカウントします。JSONのようなrawボディは対象外です。
実測(2026-09-18): プロキシ経由で3 MiBマルチパートを送信すると、ボディ全体が送信されアプリケーション認証層まで到達します(サイズではなく 401 で終了します)。転送経路に2 MiBの壁はなく、nginx は client_max_body_size 200m です。
2026-09-18以前は443コネクタのみ maxPostSize="-1" であり、同じサーバーがポートによって異なる上限を強制していました。現在は80 · 443とも同じです。
ヘルスチェック
# 표준 헬스체크 (익명, 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
頻発する問題
| 症状 | 原因 | 対処 |
|---|---|---|
OutOfMemoryError | ヒープ不足 | setenv.sh で -Xmx を増量 + GCログ分析 |
Too many open files | ファイルディスクリプタ上限 | /etc/security/limits.conf の nofile 65535以上 |
| コンソール応答遅延 | DBスロークエリ | PostgreSQL slow queryログ、Valkeyキャッシュヒット率確認 |
| ログイン無限ループ | セッション / クッキー問題 | ブラウザキャッシュ削除、server/conf/Catalina/localhost/ROOT.xml の sessionCookieName 確認 |
| 502 / 504 (リバースプロキシ) | バックエンド応答時間超過 | nginx proxy_read_timeout を増加 |
| WebSocket切断 | ファイアウォールidle timeout | ファイアウォールtimeout > 60s、tomcat.websocket.session.timeout 調整 |
詳細な診断は問題解決ページを参照してください。
セキュリティチェックリスト
- デフォルトadminパスワード変更 (
admin / admin123!) -
tomcat-users.xmlのmanagerアカウント削除または強力なパスワード設定 -
server.xmlのshutdown port (7000)外部アクセス遮断 - HTTPS証明書適用 (
plantpulse-certsが焼き込んだpp-securityボリュームの資材または外部CA) - X-Frame-Options / CSP / HSTSヘッダー設定確認
- API Bearer Tokenを運用vaultに保存
-
config/プロパティファイルのデバッグオプション無効化および権限制限 (chmod 600)
詳細なセキュリティ設定はセキュリティ設定ページを参照してください。