시스템 시작 및 종료
개요
PlantPulse 는 Docker Compose 스택으로 동작합니다. 시작 / 종료 / 재시작은 /opt/kopens/plantpulse-platform-docker/bin/ 의 공통 동사 한 곳에서 관리하며, 컨테이너 간 의존성 순서는 compose 의 depends_on 이 자동으로 적용하므로 운영자가 일일이 순서를 신경 쓸 필요는 없습니다.
up.sh 와 restart.sh 의 0 은 «명령이 성공했다» 가 아니라 «이제 쓸 수 있다» 입니다. 2026-08-14 자격증명 회전 때 «시작됨» 만 보고 다음 단계로 넘어갔다가 아직 뜨지 않은 브로커에서 멈춘 사고가 있어, 두 동사가 준비될 때까지 기다리도록 바뀌었습니다.
status.sh 는 다릅니다 — 0 = 정상 / 2 = 비정상 입니다.
권장 운영 명령 (요약)
cd /opt/kopens/plantpulse-platform-docker/bin
./up.sh # 기동 (멱등) — 준비될 때까지 대기. 0 = 준비 완료
./down.sh # 안전 종료 (의존 순서의 역순, 상태 보존)
./restart.sh # graceful drain → 정지 → 기동 → 준비 대기
./status.sh # 서비스 / health / 볼륨 요약. 0 = 정상 / 2 = 비정상
./ops-check.sh # 헬스 + 최근 critical log
./logs.sh [서비스] # 로그 보기 — 1회 출력, -f 로 따라가기 (인자 없으면 전체)
./stack-verify-boot.sh # 스택 전체 준비 판정
./doctor.sh # 진단 tarball (시스템 변경 없음)
모든 동사가 --help 를 지원합니다. 옛 이름(stack-run.sh · stack-stop.sh · stack-bash.sh · stack-update.sh · stack-remove.sh)도 그대로 동작합니다.
| 환경 변수 | 기본값 | 뜻 |
|---|---|---|
PP_READY_TIMEOUT | 900 | 준비 대기 상한(초) |
PP_READY_INTERVAL | 15 | 확인 간격(초) |
PP_WAIT=0 | — | 대기하지 않음. 이때의 0 은 준비 완료를 뜻하지 않습니다 |
시작 순서 (자동)
컨테이너 사이
compose 의 depends_on 이 service_healthy 조건으로 순서를 지킵니다 — «프로세스가 떴다»가 아니라 «쓸 수 있다»를 기다립니다. 이 순서는 임의가 아니라 애플리케이션 오케스트레이터의 기동 순서를 그대로 옮긴 것입니다.
마지막 넷(warehouse · opcua · aasx · ha)은 서로 순서가 없습니다.
데이터레이크 컨테이너 안
인프라 컴포넌트는 plantpulse-datalake 한 컨테이너 안에서 다시 순서대로 기동합니다.
| 순서 | 영역 | 주요 컴포넌트 | 대표 포트 |
|---|---|---|---|
| 1 | 스토리지 | Aster TSDB, PostgreSQL, Valkey, RustFS | 9042 / 5432 / 6379 / 9000 |
| 2 | 분석 | Spark, Hive, Kyuubi, Gravitino | 7077 / 9083 / 10000 / 19001 |
| 3 | 시계열 | TSE Engine, UI | 7800 / 3000 |
| 4 | 메시징 | Kafka, MQTT | 9092 / 1883 |
| 5 | 워크플로우 | Temporal, Kestra | 7233 / 8233 / 8380 |
| 6 | 처리 | CEP, Data Gateway, SQL, Monitor | 7400 / 5500 / 4000 / 4950 |
프로세스 기동 자체는 35분(JVM 워밍업)이지만, 클린 설치는 Aster TSDB 스키마 마이그레이션과 안정화 때문에 **전 컴포넌트가 안정되기까지 1518분**이 걸립니다.
실측(2026-08-31, 32 vCPU / 128GiB): 데이터레이크 217초, 웹 서버 316초. 데이터가 이미 있는 재시작은 훨씬 빠릅니다.
종료 순서
cd /opt/kopens/plantpulse-platform-docker/bin
./down.sh
순서를 손으로 지킬 필요가 없습니다. compose 가 의존 순서의 역순으로 정지하므로, 데이터를 쓰는 쪽(앱)이 먼저 멈추고 데이터레이크가 마지막에 멈춥니다. 각 컨테이너에는 넉넉한 정지 유예(stop_grace_period)가 걸려 있어 — 데이터레이크는 180초 — Aster TSDB 가 Memtable 을 플러시할 시간을 확보합니다.
볼륨(pp-data · pp-temp · pp-backup · pp-security · pp-proxy-certs)은 정지·제거와 무관하게 항상 보존됩니다.
docker kill 이나 호스트 강제 종료를 쓰지 마세요Aster TSDB 의 플러시되지 않은 Memtable 데이터가 유실될 수 있습니다. 반드시 ./down.sh 를 사용해 주세요. 이미 컨테이너가 응답하지 않는다면 비상 절차를 따르세요.
재시작
전체 재시작
cd /opt/kopens/plantpulse-platform-docker/bin
./restart.sh
- (돌고 있다면) 데이터레이크에 graceful drain 요청
- 스택 전체를 의존 순서의 역순으로 정지
depends_on순서로 다시 기동- 준비될 때까지 대기 — 종료 코드
0이면 쓸 수 있는 상태
bin/env.sh 변경, 로케일·타임존 변경, 일시적 문제 해소, 정기 재시작에 사용합니다.
앱 하나만 재시작
앱 여섯은 각자 컨테이너에서 돌기 때문에, 그 컨테이너만 다시 띄우면 나머지 앱과 인프라는 그대로 살아 있습니다. 이것이 컨테이너를 나눈 이유 중 하나입니다.
cd /opt/kopens/plantpulse-platform-docker
docker compose -f compose/docker-compose.yml restart plantpulse-server-web
docker compose restart 는 의존 순서를 지키지 않습니다여러 앱을 함께 되살려야 한다면 ./restart.sh 로 스택 전체를 재시작하는 편이 안전합니다.
인프라 컴포넌트만 재시작
./shell.sh # 데이터레이크 진입
/opt/kopens/plantpulse-platform/plantpulse-datalake-cli/bin/pd restart storage
exit
pd restart <서비스> 가 받는 이름은 services/order.txt 에 있는 여덟뿐입니다 — storage · analytics · messaging · timeseries · cep · workflow · data-gateway · admin-api. 그 밖의 이름은 unknown service 로 끝납니다. 자세한 내용은 시작 가이드를 참고해 주세요.
상태 확인
한 줄 요약
./status.sh # 0 = 정상 / 2 = 비정상
status.sh 가 비정상으로 보는 것은 다섯 가지입니다.
| 판정 | 내용 |
|---|---|
| 서비스 목록을 못 읽음 | compose 해석 실패 또는 docker 접근 불가 |
| compose 가 선언한 서비스에 컨테이너가 없음 | 기동되지 않음 |
컨테이너가 running 이 아님 | 아홉 개 전부 상시 동작합니다 — 제외 대상이 없습니다 |
상시 컨테이너의 health 가 unhealthy | starting / none 은 판정 보류 |
| 헬스 API 가 OK 가 아님 | 데이터레이크 안에서 probe |
볼륨과 conf 는 보고만 하고 종료 코드에 넣지 않습니다 — 설치 구성에 따라 일부가 없는 것이 정상이기 때문입니다.
plantpulse-certs 는 인증서를 한 번 굽고 계속 떠 있으면서 만료를 감시합니다. 아홉 개를 같은 기준(healthy)으로 보면 됩니다.
헬스체크는 이미지가 갖고 있습니다. compose 에 probe 블록이 없는 것은 의도한 것이고 pp.healthcheck: image 라벨이 그것을 명시합니다. 프로브 경로가 다른 저장소의 것이라 compose 에 적어 두면 그쪽이 파일을 옮기는 순간 고객사에서 깨집니다.
컨테이너 확인
docker ps # 아홉 개가 모두 (healthy)
docker stats --no-stream # 컨테이너별 CPU / 메모리
헬스체크 API
# 호스트 / 외부에서 — 4950 이 publish 되어 있습니다
curl -kfsS https://<서버IP>:4950/api/health | jq
# 컨테이너 안에서 — 어떤 구성에서도 동작합니다
docker exec plantpulse-datalake curl -kfsS https://127.0.0.1:4950/api/health | jq
"status" 값은 OK · WARN · FAIL 셋뿐이며, OK/WARN 이 정상 범위입니다.
콘솔과 헬스 API 는 두 포트 모두에서 서비스됩니다 — 4950(HTTPS)와 4949(평문 HTTP). 같은 콘솔·같은 API 이고 스킴만 다릅니다. 4949 는 더 이상 4950 으로 리다이렉트하지 않습니다.
4949 는 평문입니다 — 로그인 비밀번호와 세션 쿠키가 그대로 흐릅니다. 신뢰할 수 없는 망에서는 4950 을 쓰세요. 4949 는 자체 서명 인증서 경고가 실제로 운영자를 막아 세우는 상자를 위한 선택지입니다.
로그 확인
./logs.sh # 여덟 컨테이너를 시간순으로 한 화면에
./logs.sh plantpulse-server-web -n 200 # 특정 컨테이너
./logs.sh cassandra # 데이터레이크 안 컴포넌트 로그 파일
./logs.sh --list # 볼 수 있는 대상 전체
./logs.sh -f plantpulse-proxy # 계속 따라가기 (Ctrl-C 로 종료)
기본은 마지막 N줄(기본 200)을 찍고 끝입니다. 따라가려면 -f 를 붙이세요.
자동 시작 설정
별도 설정이 필요하지 않습니다. 컨테이너는 compose 에 재시작 정책이 선언되어 있어 호스트를 재부팅해도 Docker 데몬이 뜨면 함께 올라옵니다. plantpulse-certs 는 restart: unless-stopped 입니다 — 사람이 일부러 멈춘 경우에는 데몬을 재시작해도 멈춘 채로 둡니다. 재시작해도 인증서를 새로 굽지 않습니다.
확인할 것은 Docker 데몬의 자동 시작 하나뿐입니다.
systemctl is-enabled docker # enabled 여야 합니다
sudo systemctl enable docker # 아니라면
호스트 재부팅은 depends_on 순서가 적용되지 않는 유일한 경로입니다 — 전부 한꺼번에 올라오기 때문에, 웹 서버가 뜰 때까지(실측 최대 316초) 프록시의 헬스체크가 잠시 실패할 수 있습니다. 재시도 예산이 360초로 잡혀 있고 docker 는 unhealthy 를 이유로 컨테이너를 재시작하지 않으므로, 스스로 회복되는 일시적 상태입니다.
비상 절차
웹 콘솔이 응답하지 않음
cd /opt/kopens/plantpulse-platform-docker/bin
# 1. 어느 컨테이너가 문제인가
./status.sh
# 2. 프록시와 웹 서버 로그
./logs.sh plantpulse-proxy -n 100
./logs.sh plantpulse-server-web -n 200
# 3. 웹 서버만 재시작 (인프라는 유지)
cd /opt/kopens/plantpulse-platform-docker
docker compose -f compose/docker-compose.yml restart plantpulse-server-web
# 4. 회복되지 않으면 진단 번들
bin/doctor.sh
프록시의 헬스체크는 자기 자신 + 업스트림 도달까지만 봅니다(백엔드의 건강 상태는 웹 서버 자신의 헬스체크가 판정합니다). 프록시가 초록인데 화면이 안 나온다면 plantpulse-server-web 쪽을 보세요.
OOM (Out Of Memory) 발생
# 1. 어느 컨테이너가 OOM 인가
docker inspect plantpulse-server-web --format '{{.State.OOMKilled}} {{.State.ExitCode}} {{.RestartCount}}'
# 2. 호스트 커널 로그
dmesg | grep -i "out of memory\|oom"
# 3. 컨테이너별 사용량
docker stats --no-stream
컨테이너마다 한계값이 따로 있습니다 — 데이터레이크는 DOCKER_DATALAKE_MEMORY(기본 96G), 앱은 DOCKER_SERVER_MEMORY · DOCKER_BATCH_MEMORY 등입니다(환경 변수). 값을 조정한 뒤 ./restart.sh 로 적용합니다.
앱마다 mem_limit 을 따로 두는 것이 컨테이너 분리의 첫 번째 목적입니다. 한 앱이 한계에 닿아도 인프라와 다른 앱은 살아 있습니다.
DB 연결 실패
데이터베이스는 모두 plantpulse-datalake 안에 있습니다.
cd /opt/kopens/plantpulse-platform-docker/bin
./shell.sh # 데이터레이크 진입
/opt/kopens/plantpulse-platform/plantpulse-datalake-cli/bin/pd node psql # PostgreSQL
/opt/kopens/plantpulse-platform/plantpulse-datalake-cli/bin/pd node cql # Cassandra
/opt/kopens/plantpulse-platform/plantpulse-datalake-cli/bin/pd node status # Cassandra 링 상태
인프라만 되살리려면 컨테이너 안에서 pd restart storage 를, 그래도 안 되면 호스트에서 ./restart.sh 를 사용합니다.
데이터 손상 의심 시
cd /opt/kopens/plantpulse-platform-docker/bin
./down.sh # 즉시 정지 (추가 손상 방지)
ls -lh /data1/pp-backup/docker-volume/ # 최신 백업 확인
./doctor.sh # 진단 번들 (데이터를 임의로 수정하지 마세요)
생성된 tarball 을 webmaster@kopens.com 으로 전달해 주세요.
운영 체크리스트
일일 점검
| 점검 항목 | 명령어 / 확인 방법 | 정상 기준 |
|---|---|---|
| 전체 서비스 상태 | ./status.sh | 종료 코드 0 |
| 운영 헬스 | ./ops-check.sh | critical 로그 없음 |
| 헬스 API | curl -kfsS https://<서버IP>:4950/api/health | status 가 OK 또는 WARN |
| 컨테이너 | docker ps | 아홉 개 모두 (healthy) |
| 디스크 사용량 | df -h /data1 | 사용률 80% 미만 |
| 컨테이너 자원 | docker stats --no-stream | 한계값 대비 여유 있음 |
| Aster TSDB 상태 | 컨테이너 안에서 pd node status | 모든 노드 UN (Up/Normal) |
주간 점검
| 점검 항목 | 명령어 / 확인 방법 | 정상 기준 |
|---|---|---|
| Aster TSDB Compaction | 컨테이너 안에서 pd node compactionstats | Pending 작업 과다 아님 |
| Aster TSDB 테이블 | 컨테이너 안에서 pd node table-stats | 비정상 증가 없음 |
| Kafka 토픽 / lag | 컨테이너 안에서 pd node topic | 지연 메시지 수 정상 범위 |
| 백업 확인 | ls -lh /data1/pp-backup/docker-volume/ | 정상 백업 존재 |
| Docker 사용량 | docker system df | 미사용 이미지 누적 없음 |
| 로그 용량 | du -sh /opt/kopens/plantpulse-platform-docker/logs | 비정상적 증가 없음 |
| 보안 업데이트 | OS 패키지 업데이트 확인 | 알려진 취약점 없음 |