pd — 데이터레이크 CLI
pd(PlantPulse DataLake)는 데이터레이크 컨테이너 안에서 서비스 여덟 개를 기동 · 정지 · 진단하고, 설정을 렌더하고, 백업을 찍는 단일 진입점 명령입니다. 관리 콘솔이 보여 주는 거의 모든 것이 이 명령의 --json 출력입니다.
| 항목 | 값 |
|---|---|
| 모듈 | plantpulse-datalake-cli |
| 위치 | 컨테이너 안 /opt/kopens/plantpulse-platform/plantpulse-datalake-cli/bin/pd (PATH 에 있음) |
| 호스트에는 | 없습니다 — 반드시 docker exec plantpulse-datalake pd … |
docker exec plantpulse-datalake pd status # 하나만 칠 때
/opt/kopens/plantpulse-platform-docker/bin/shell.sh # 여러 개 칠 때 — 셸을 열고 pd … 만 친다
플랫폼 전체의 시작 / 정지 / 재시작은 호스트의 /opt/kopens/plantpulse-platform-docker/bin/ 에서 합니다 — up.sh · down.sh · restart.sh · restart-datalake.sh · status.sh 입니다. pd 는 그보다 한 겹 아래, 컨테이너 안의 서비스를 다루는 도구입니다. 컨테이너 자체도 켜질 때 pd start, 꺼질 때 pd stop 을 돌리므로 운영자가 pd start 를 손으로 칠 일은 드뭅니다. 대부분 «상태 보기» 와 «서비스 하나 재기동» 입니다.
30초 요약
안전 등급: 읽기 = 아무것도 안 바꿈 · 변경 = 상태를 바꿈 · 파괴적 = 데이터나 프로세스를 지움.
| 동사 | 한 줄 | 등급 |
|---|---|---|
pd status [서비스] [--wait] | 포트 표. 전 행 RUNNING 일 때만 exit 0 | 읽기 |
pd doctor | 환경 · 시크릿 · 템플릿 · 런타임 트리 검사 (PASS / FAIL / N/A) | 읽기 |
pd env | 이 노드의 디렉터리 · 이미지 정체 — 선언만, 측정 없음 | 읽기 |
pd version | 설치된 것에서 읽은 판 번호 — 이 파일의 상수가 아니라 jar MANIFEST 에서 | 읽기 |
pd logs [--list] [--lines N] [서비스…] | 서비스 로그 따라가기 | 읽기 |
pd storage | 볼륨 · WAL · 복제 슬롯 · 저장소 크기 · Kafka 보존 | 읽기 |
pd retention | 테이블 TTL · 토픽 보존 · 콜드 티어 · 아카이브 잡 | 읽기 |
pd flow | Kafka 컨슈머 그룹 lag | 읽기 |
pd downtime | 계획 밖으로 죽어 있던 구간 — 재기만 한다 | 읽기 |
pd config list · diff · diff --templates | 템플릿 → 경로 표 · 런타임 vs 렌더 · 호스트 템플릿 vs 이미지 기본값 | 읽기 |
pd config show <대상> | 서비스가 실제로 읽는 렌더된 파일. 자격증명은 가려집니다 | 읽기 |
pd workflow status · list · history | 워크플로우 엔진 둘(Temporal · Kestra) — 엔진과 폴러, 쥐고 있는 스케줄, 최근 실행과 실패 사유. 읽기 전용 | 읽기 |
pd node status · info · cql · psql … | Aster TSDB / PostgreSQL 조회와 셸 | 읽기 |
pd backup list · schedule · status | 물리 백업 세트 · 스케줄 표 · 지금 도는 것 | 읽기 |
pd backup restore … --dry-run | 복원 계획만 | 읽기 |
pd start [서비스] · stop · restart [--clean] | 순서대로 기동 · 역순 정지(정지 증명) · 둘 다 | 변경 |
pd config render | 설정 생성물을 실제로 쓴다 | 변경 |
pd secret rotate VAR=값 | 자격증명 회전 — 호스트의 passwd.sh 가 부릅니다. 직접 치지 마세요 | 변경 |
pd secret probe-mqtt | 준 자격증명으로 MQTT 브로커에 실제로 붙어 본다(회전 절차가 쓰는 검증) | 읽기 |
pd backup · pd backup run --engine E --type full|diff | 논리 덤프 · 물리 백업 한 번 | 변경 |
pd backup schedule set|reset | 백업 잡의 Quartz cron 과 켜짐 스위치 | 변경 |
pd node add · rf · repair · compact · flush · drain … | Aster TSDB 정비 — 노드를 넣고 뺄 때는 pd node rf 순서를 지키세요 | 변경 |
pd clean | 멈춘 모듈의 로그 · 임시 파일 삭제 | 파괴적(로그) |
pd kill [--dry-run] | PP_HOME 아래 프로세스 전부 SIGKILL | 파괴적 |
pd recover [파일] | pp DB 를 DROP 하고 논리 덤프로 복원 | 파괴적 |
pd backup restore … --yes | 데이터 디렉터리를 물리 백업으로 되돌림 | 파괴적 |
pd node cleanup · remove <host-id> | 스냅샷 삭제 · 링에서 노드 제거 | 파괴적 |
옵션 셋은 어느 동사에나 같은 뜻입니다.
| 옵션 | 뜻 |
|---|---|
--json | 사람용 표 대신 JSON 문서 하나. 콘솔이 읽는 형식. 받는 동사가 정해져 있고 다른 동사에 붙이면 exit 2 로 거부 |
--show-secrets | config diff 만. 비밀번호를 *** 로 가리지 않음. --json 과 함께 쓸 수 없음 |
PD_DEBUG=1 | DEBUG 로그. PD_DEBUG=1 pd start 처럼 앞에 붙임 |
서비스와 기동 순서
서비스 여덟 개는 정해진 순서로 뜹니다. 정본은 services/order.txt 하나입니다. pd start 는 위에서 아래로, pd stop 은 역순입니다.
| 순서 | 서비스 | MASTER | WORKER | 안의 컴포넌트 | pd status 행(포트) |
|---|---|---|---|---|---|
| 1 | storage | ○ | ○ | valkey → postgres → cassandra → rustfs(MASTER 만) | 6379 · 5432 · 9042 · 9000 |
| 2 | analytics | ○ | ○ | spark-master → hive → gravitino → kyuubi (WORKER 는 spark-worker · kyuubi) | 7077 · 4440 · 9083 · 19001 · 10000 |
| 3 | messaging | ○ | — | kafka · mqtt(HiveMQ) | 9092 · 1883 |
| 4 | timeseries | ○ | — | engine(TSE) · dashboard(PlantPulse Dashboard) | 7800 · 3000 |
| 5 | cep | ○ | — | Tomcat | 7400 |
| 6 | workflow | ○ | — | temporal → kestra | 7233 · 8233 · 8380 |
| 7 | data-gateway | ○ | — | Tomcat | 5500 |
| 8 | admin-api | ○ | ○ | 관리 콘솔 백엔드 | 4949 |
- 각 서비스를 띄운 뒤 헬스체크가 UP 을 돌려줄 때까지 기다렸다가 다음으로 갑니다(상한: storage 1800초, analytics 600초, 나머지 300초).
storage와analytics는 문지기입니다. 안 뜨면 뒤를 시도하지 않고 exit 5. 나머지는 경고하고 계속(끝에 exit 6).timeseries는 2026-09-05 부터 MASTER 전용입니다.
수명주기
pd status # 전체 표 — 마지막 줄 "0 STOPPED" 면 정상
pd status storage # 서비스 하나의 헬스체크
pd status storage --wait # UP 이 될 때까지 대기
pd start cep # 죽은 서비스 하나 다시 띄우기
pd stop cep # 서비스 하나 내리기 — «멈췄다» 를 증명한다 (30초 + SIGTERM 15초 + SIGKILL 10초)
pd restart cep # stop → (정지가 증명되면) 5초 → start
pd restart cep --clean # 사이에 pd clean
pd restart # 전부 — 몇 분 걸린다. storage 가 먼저 돌아온다
pd status 의 상태는 넷입니다.
| 상태 | 뜻 |
|---|---|
RUNNING | 포트가 열려 있다 |
STOPPED | 포트가 닫혀 있다. 기동 직후 몇십 초는 창(window)이니 1분 뒤 다시 본다 |
UNKNOWN | 못 쟀다 — 포트를 재는 도구가 없을 때. 죽은 것이 아니다 |
DISABLED | PD_OPTIONS 로 껐다. 종료 코드를 바꾸지 않는다 |
pd stop 이 exit 7 이면 바로 pd start 하지 마세요STILL RUNNING 은 정지를 증명하지 못했다는 뜻입니다. 살아남은 프로세스 위에 띄우면 포트 충돌 · 데이터 손상이 납니다. pd kill --dry-run 으로 무엇이 남았는지 본 뒤 pd kill, pd status 로 비었음을 확인하고 pd start. FORCE=1 pd start 는 쓰지 마세요.
컨테이너 자체의 Docker HEALTHCHECK 는 pd status 를 부르지 않습니다(느리고 가끔 흔들려서). 대신 postgres · cassandra 가 실제 쿼리에 답하나와 포트 전부가 열려 있나를 봅니다. 그래서 «docker ps 는 healthy 인데 pd status 는 STOPPED» 는 기동 창이거나 한 번 빗나간 것이고, «unhealthy 인데 pd status 는 전부 RUNNING» 이면 포트는 열렸지만 쿼리에 답을 못 하는 상태입니다(pd logs storage).
진단
pd doctor # 여섯 절 검사. 마지막 줄 FAIL 0 이면 된다
pd env # 디렉터리 · 이미지 정체 (0.25초)
pd logs --list # 따라갈 파일 목록만
pd logs --lines 50 cep # cep 만, 마지막 50줄부터
pd storage # 볼륨 90% 이상이면 FAIL
pd retention # 왜 안 줄어드나 — TTL · 토픽 보존 · 콜드 티어
pd flow # 데이터가 안 들어온다 — 컨슈머 lag
pd downtime # 자꾸 죽는 것 같다 — 계획 밖 정지 기록
pd doctor 의 여섯 절: [1] inputs(시크릿 · 노드 파일 · 이미지) · [2] tools · [3] config templates(전부 렌더되나) · [4] runtime tree · [5] TLS material · [6] core ports. N/A 는 «여기서는 잴 수 없다» 이고 괄호에 이유가 붙습니다. 컨테이너 안에서 사이드카 파일이 없는 것은 정상입니다 — 값은 환경변수로 옵니다.
pd flow 의 NA 는 «뒤처졌다» 가 아니라 «한 번도 읽은 적이 없다» 입니다. MEMB 열이 0 이면 소비자가 안 떠 있는 것, 0 보다 큰데 토픽이 비었으면 정상(읽을 게 없음), 0 보다 큰데 토픽에 데이터가 있으면 진짜 의심 신호입니다.
설정
pd config list # 이 모드의 템플릿 → 경로 표
pd config diff # 런타임 파일 vs 지금 렌더하면 나올 것 (0 같음 / 1 다름 / 3 시크릿 없음 / 4 렌더 실패)
pd config diff --templates # 호스트 템플릿 vs 이미지 기본값 (same / differs / local / missing)
pd config render # 실제로 쓴다 — 그 뒤 pd restart <서비스> 까지가 한 세트
값을 바꾸는 절차와 «어디를 고쳐야 하나» 는 설정 바꾸는 법에 있습니다. 컨테이너 안 생성물을 직접 고치지 마세요 — 다음 pd start 에 사라집니다.
Aster TSDB · PostgreSQL 노드 조작
pd node status # nodetool status — UN 이 정상, DN 이면 죽은 노드
pd node status --json # + PostgreSQL 복제 · Valkey 복제 · 워커 명부
pd node info # 노드 상세
pd node cql # cqlsh (cassandra 계정)
pd node psql # psql (postgres OS 사용자)
pd node errors # cassandra debug.log 의 최근 WARN/ERROR
pd node topic # kafka 토픽 "event" describe
pd node tpstats | compactionstats | proxyhistograms | table-stats [ks] | table-histograms <ks> <tbl> | sstable-size <ks> <tbl> | disk
| 변경하는 동사 | 등급 | 한 줄 |
|---|---|---|
pd node add | 변경 | 살아 있는 노드 수에 맞춰 키스페이스 RF 를 올리고(최대 3) repair. 워커를 붙인 뒤 마스터에서 한 번 |
pd node repair · repair-table <ks> <tbl> | 변경(무겁다) | 노드 간 데이터 불일치를 맞춘다 |
pd node flush · drain | 변경 | 메모리의 쓰기를 디스크로. drain 은 그 뒤 쓰기를 거부하므로 정지 직전에만 |
pd node compact [ks] [tbl] | 변경(무겁다) | SSTable 병합 |
pd node cleanup | 파괴적 | 스냅샷 전부 삭제 + 이 노드가 더 이상 소유하지 않는 데이터 삭제 |
pd node remove <host-id> | 파괴적 | 죽은 노드를 링에서 뺀다. 살아 있는 노드에 쓰면 안 된다. RF 를 먼저 내린다 — 아래 |
pd node rf [--check] [<노드수>] | 변경 | 링을 재고 모든 키스페이스의 복제 계수(RF) 를 그 수에 맞춘다 |
pd node upgrade · init-cms · train-zstd · cache-clear | 변경 | 설치 절차 · 버전업 · OS 페이지 캐시 비우기 |
pd node rf — 복제 계수를 링에 맞춘다
노드를 넣거나 뺄 때 RF 를 손으로 고치지 마세요. 이 한 동사가 링을 재고, 목표 RF 를 계산하고, 모든 키스페이스에 적용합니다. 방향마다 부르는 자리가 정해져 있습니다.
| 언제 | 순서 |
|---|---|
| 늘릴 때 | pd node add → pd node rf → nodetool repair |
| 줄일 때 | pd node rf <빠진 뒤의 노드수> → 노드가 떠남 → nodetool cleanup |
| 죽은 노드 | pd node rf → pd node remove <host-id> |
깨진 상태는 RF 가 노드 수보다 큰 것입니다. 먼저 내리면 그 상태에 들어가지 않습니다.
반대 순서로 하면 RF 3 이 2노드 링에 얹히는 창이 생기고, 그 사이에 한 노드만 더 잃으면
QUORUM 읽기·쓰기가 전부 깨집니다. system_auth 까지 포함이라 — 아무도 로그인해서
무슨 일이 났는지 볼 수조차 없습니다.
nodetool decommission·removenode 는 떠나는 노드의 레인지를 현재 RF 기준으로 흘려
보냅니다. 먼저 내리면 흘려보낼 데이터가 줄고, 남은 노드가 새 RF 가 말하는 만큼만
소유하게 됩니다.
<노드수> 인자가 있는 이유가 이 순서입니다 — RF 를 줄일 때 떠날 노드가 아직 링 안에
있으므로, 목표는 «그 노드가 나간 뒤의 수» 입니다. 이미 죽어 링 밖인 노드를 remove 할
때는 인자 없이 두면 됩니다.
잠깐 꺼져 있는 노드 때문에 RF 가 내려가지 않습니다. 구성원 3에 살아 있는 것이 2면 경고를 찍되 RF 는 3으로 둡니다 — 노드가 돌아오면 원래대로이기 때문입니다.
노드가 셋 미만이면 RF 도 그만큼입니다(1노드 → RF 1). 세 번째 노드가 붙고 pd node rf
를 다시 돌리면 3으로 오릅니다. 노드 수보다 큰 RF 는 모든 QUORUM 질의를 실패시키므로
그렇게 두지 않습니다.
--check 는 재기만 합니다pd node rf --check 는 아무것도 바꾸지 않고 지금 RF 가 링에 맞는지만 봅니다.
못 쟀을 때는 「맞다」가 아니라 «판정 못 함»으로 끝납니다(종료 코드 2) — 데이터센터 이름을
못 읽거나 노드 수를 못 세면 짐작하지 않습니다. 이름을 잘못 짚으면 복제가 조용히
사라지기 때문입니다.
pd 가 모르는 복제 전략을 쓰는 키스페이스는 건드리지 않고 이름을 찍어 줍니다 —
조용히 지나가지 않습니다.
올릴 때 repair, 내린 뒤 cleanup 인 것도 이유가 있습니다. 새 복제본은 비어 있는 채로
시작하므로 채워야 하고(repair), 내린 뒤에는 더 이상 소유하지 않는 레인지를 버려야
합니다(cleanup).
백업 · 복원
pd backup # PostgreSQL 논리 덤프 → /data1/pp-data/postgres/dump/
pd backup list | schedule | status
pd backup run --engine postgres --type diff
pd backup restore --engine postgres --set <세트> --dry-run # 계획 먼저
pd recover [덤프파일] # pp DB 하나를 논리 덤프로 되돌림 (파괴적)
절차는 백업 · 복원에 있습니다.
종료 코드
| 코드 | 뜻 |
|---|---|
| 0 | 성공. status 는 전부 RUNNING, doctor 는 FAIL 0, config diff 는 차이 없음 |
| 1 | 일반 실패 · «다르다» · «문제 있다» |
| 2 | 사용법 오류 — 모르는 동사 · 서비스 · 옵션, --json 거부, PP_HOME 미명시 |
| 3 | 필수 시크릿 누락 — 메시지에 이름 전부. backup run · restore 에서는 «다른 백업이 돌고 있다» |
| 4 | 렌더 실패 — 치환 안 된 ${PP_*}, 이름 전부 나열 |
| 5 | 문지기 서비스(storage · analytics)가 안 떠서 기동 중단 |
| 6 | 기동은 끝났는데 서비스 일부가 준비 안 됨 |
| 7 | 정지를 증명하지 못함(STILL RUNNING) |
| 8 | 광고 주소가 루프백(127.0.0.1) — 렌더 거부 |
로그 형식과 저널
pd 가 찍는 모든 줄은 한 모양입니다 — [시각] [DATALAKE-CLI] [레벨] [동사] 메시지. INFO 는 stdout, 나머지는 stderr 로 갑니다.
pd start · stop · restart · backup 은 JSON 한 줄씩을 이벤트 저널 plantpulse-datalake-admin-api/logs/pd-events.jsonl 에 남기고, 콘솔이 그것을 이벤트 타임라인으로 보여 줍니다. 콘솔에서 실행한 명령은 actor 가 operator:<이름> 으로 남습니다.
PD_OPTIONS — 컴포넌트를 골라 끄기
변수는 있지만 현재 compose 스택은 이 값을 컨테이너에 넘기지 않습니다. 그래서 지금은 컴포넌트를 골라 끄는 구성이 지원되지 않습니다 → FAQ
서버 · 배치 · 웨어하우스 · OPC-UA · AASX 는 각자의 컨테이너에서 돕니다. 데이터레이크 컨테이너에는 그 모듈이 없습니다. 앱은 호스트에서 재시작하세요.
cd /opt/kopens/plantpulse-platform-docker/bin
./restart-server.sh # 또는 restart-batch.sh · restart-warehouse.sh · restart-one.sh <서비스>
관련 문서
- 시작 · 정지 · 재시작 — 호스트 동사와
pd의 관계 - 로그와 헬스
- 설정 바꾸는 법