본문으로 건너뛰기

plantpulse-server (웹 콘솔 + IIoT 엔진)

역할

플랫폼의 얼굴이자 두뇌에 해당하는 핵심 모듈입니다. 운영자가 보는 웹 콘솔, 외부 시스템이 호출하는 REST API, 그리고 산업 데이터를 실제로 처리하는 IIoT 엔진이 모두 이 한 모듈 안에서 동작합니다.

항목
모듈명plantpulse-server
컨테이너plantpulse-server-web (자기 이미지, 자기 컨테이너)
컨테이너 안 경로/opt/kopens/plantpulse-platform/plantpulse-server/
사용자 접속80 / 443plantpulse-proxy 가 받아 이 컨테이너로 넘깁니다
관리 콘솔7443 (HTTPS, plantpulse-datalake 가 publish)
호스트 포트없음 — 프록시 뒤에서만 동작합니다
메모리 한계DOCKER_SERVER_MEMORY 기본 16g / 힙 DOCKER_SERVER_HEAP 기본 12g
런타임Apache Tomcat 9 + 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 # 메시징 연결
│ ├── plantpulse-mail.properties # SMTP
│ ├── plantpulse-ai.properties # AI 게이트웨이
│ └── 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-web exploded 디렉토리 방식은 폐기 예정이며, server.xml 의 Context docBase 오버라이드는 제거되었습니다. ROOT/ 내부 파일을 직접 수정하면 다음 배포 때 사라지므로, 설정 변경은 항상 config/ 에서 합니다.

주요 설정 파일

설정 파일은 모듈의 config/ 디렉토리에 있으며, 서버는 -Dpp.conf.dir${catalina.base}/../config → 클래스패스(fallback) 순으로 탐색합니다. 외부 설정이 로드되면 기동 로그에 Properties loaded from external conf: 가 출력됩니다.

application.properties 는 제거되었습니다 (2026.06). 테마 / 홈페이지 등 콘솔 동작 설정은 콘솔의 시스템 > 설정 관리 (mm_config 테이블)에서 관리하고, alarm.duplicate.check.minutesplantpulse-engine.properties 로 이관되었습니다.

plantpulse-engine.properties

IIoT 엔진 파이프라인 튜닝.

항목기본값설명
engine.pipeline.threads36병렬 처리 스레드 수
engine.pipeline.ratelimit40000초당 최대 메시지
engine.pipeline.batch.size1000배치 묶음 크기
engine.pipeline.queue.size1200000큐 용량
engine.dedup.enabledtrue중복 제거 활성화

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}

이 앱은 설정을 렌더링하지 않습니다. 컨테이너로 갈라진 뒤 웹 서버는 데이터레이크의 설정 렌더러 없이 뜨며, 값을 세 곳에서 읽습니다 — ① 운영자가 둔 외부 파일 → ② WAR 에 내장된 기본값 → ③ compose 가 넘긴 환경변수. 셋 중 환경변수가 내장 기본값을 덮습니다.

그래서 앱이 보는 값의 정의처는 compose/docker-compose.yml 하나뿐입니다. 데이터레이크 쪽에만 있고 compose 에 없는 이름은 이 앱에 닿지 않습니다 — 데이터레이크에서만 식별자를 바꾼 사이트에서 앱 다섯이 옛 이름을 계속 쓴 사고가 그것입니다(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 catalinaserver/logs/catalina.outTomcat 표준 출력
액세스 로그server/logs/localhost_access_log.YYYY-MM-DD.txtHTTP 요청 로그
엔진 로그logs/engine.logIIoT 엔진 처리 상세
슬로우 쿼리logs/slow.log200ms 이상 쿼리

로그 레벨 변경 — 로깅 설정(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 조정. 대규모 동시 접속 환경에서는 다음을 권장합니다.

동시 접속maxThreadsacceptCount
~ 100200100
~ 500500200
500+1000500

IIoT 엔진 처리량

plantpulse-engine.propertiesengine.pipeline.threads 를 호스트 코어 수의 70~80% 로 설정. 메시지 적체 시 engine.pipeline.queue.capacity 도 함께 상향.

자세한 튜닝은 성능 튜닝 페이지를 참고해 주세요.

헬스 체크

# 표준 헬스체크 (익명, 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",...}

# 콘솔 ping
curl -fsS http://127.0.0.1/api/v5/ping

# 인증 필요 헬스체크
curl -fsS -u admin:admin123! http://127.0.0.1/api/v5/health

# 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.confnofile 65535+
콘솔 응답 느림DB 슬로우 쿼리PostgreSQL slow query 로그, Valkey 캐시 적중률 확인
로그인 무한 루프세션 / 쿠키 문제브라우저 캐시 삭제, server/conf/Catalina/localhost/ROOT.xmlsessionCookieName 확인
502 / 504 (리버스 프록시)백엔드 응답 시간 초과nginx proxy_read_timeout 상향
WebSocket 끊김방화벽 idle timeout방화벽 timeout > 60s, tomcat.websocket.session.timeout 조정

상세 진단은 문제 해결 페이지를 참고해 주세요.

보안 체크리스트

  • 기본 admin 비밀번호 변경 (admin / admin123!)
  • tomcat-users.xml 의 manager 계정 제거 또는 강한 비밀번호
  • server.xmlshutdown port (7000) 외부 접근 차단
  • HTTPS 인증서 적용 (prepare-ssl.sh 결과 또는 외부 CA)
  • X-Frame-Options / CSP / HSTS 헤더 설정 확인
  • API Bearer Token 운영 vault 에 저장
  • config/ 프로퍼티 파일의 디버그 옵션 비활성화 및 권한 제한 (chmod 600)

자세한 보안 설정은 보안 설정 페이지를 참고해 주세요.

관련 문서