본문으로 건너뛰기

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 flowKafka 컨슈머 그룹 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-secretsconfig diff 만. 비밀번호를 *** 로 가리지 않음. --json 과 함께 쓸 수 없음
PD_DEBUG=1DEBUG 로그. PD_DEBUG=1 pd start 처럼 앞에 붙임

서비스와 기동 순서​

서비스 여덟 개는 정해진 순서로 뜹니다. 정본은 services/order.txt 하나입니다. pd start 는 위에서 아래로, pd stop 은 역순입니다.

순서서비스MASTERWORKER안의 컴포넌트pd status 행(포트)
1storage○○valkey → postgres → cassandra → rustfs(MASTER 만)6379 · 5432 · 9042 · 9000
2analytics○○spark-master → hive → gravitino → kyuubi (WORKER 는 spark-worker · kyuubi)7077 · 4440 · 9083 · 19001 · 10000
3messaging○—kafka · mqtt(HiveMQ)9092 · 1883
4timeseries○—engine(TSE) · dashboard(PlantPulse Dashboard)7800 · 3000
5cep○—Tomcat7400
6workflow○—temporal → kestra7233 · 8233 · 8380
7data-gateway○—Tomcat5500
8admin-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못 쟀다 — 포트를 재는 도구가 없을 때. 죽은 것이 아니다
DISABLEDPD_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 가 노드 수보다 큰 것입니다. 먼저 내리면 그 상태에 들어가지 않습니다.

반대 순서로 하면 RF 3 이 2노드 링에 얹히는 창이 생기고, 그 사이에 한 노드만 더 잃으면 QUORUM 읽기·쓰기가 전부 깨집니다. system_auth 까지 포함이라 — 아무도 로그인해서 무슨 일이 났는지 볼 수조차 없습니다.

nodetool decommission·removenode 는 떠나는 노드의 레인지를 현재 RF 기준으로 흘려 보냅니다. 먼저 내리면 흘려보낼 데이터가 줄고, 남은 노드가 새 RF 가 말하는 만큼만 소유하게 됩니다.

<노드수> 인자가 있는 이유가 이 순서입니다 — RF 를 줄일 때 떠날 노드가 아직 링 안에 있으므로, 목표는 «그 노드가 나간 뒤의 수» 입니다. 이미 죽어 링 밖인 노드를 remove 할 때는 인자 없이 두면 됩니다.

RF 는 «살아 있는 수» 가 아니라 «구성원 수» 를 따릅니다

잠깐 꺼져 있는 노드 때문에 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 <서비스>

관련 문서​