> For the complete documentation index, see [llms.txt](https://kopens.gitbook.io/plantpulse-platform/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://kopens.gitbook.io/plantpulse-platform/admin/modules/workflow.md).

# workflow (워크플로우)

## 역할

장시간 실행 작업과 데이터 파이프라인 오케스트레이션. 두 개의 엔진을 함께 운영합니다.

| 엔진           | 용도                                         |
| ------------ | ------------------------------------------ |
| **Temporal** | 분산 워크플로우, 장시간 실행 작업 (재시도 / 보상 트랜잭션 / Saga) |
| **Kestra**   | 데이터 파이프라인, ETL, 스케줄 자동화 (Airflow 대안)       |

| 항목          | 값                                                      |
| ----------- | ------------------------------------------------------ |
| 모듈명         | `plantpulse-workflow`                                  |
| 설치 경로       | `/opt/kopens/plantpulse-platform/plantpulse-workflow/` |
| Temporal 포트 | 7233 (frontend), 8233 (Web UI)                         |
| Kestra 포트   | 8380                                                   |

## 구성

```mermaid
graph TB
  subgraph WF["plantpulse-workflow"]
    TP[Temporal :7233<br/>Web UI :8233]
    KE[Kestra :8380]
  end
  subgraph BACKEND["백엔드"]
    PG[(PostgreSQL<br/>workflow DB)]
  end
  subgraph CLIENT["클라이언트"]
    SVR[plantpulse-server]
    BAT[plantpulse-batch]
    USER[운영자]
  end
  USER --> TP
  USER --> KE
  SVR --> TP
  BAT --> TP
  BAT --> KE
  TP --> PG
  KE --> PG
```

## 디렉토리 구조

```
plantpulse-workflow/
├── temporal/
│   ├── bin/
│   ├── conf/
│   │   ├── server.yaml
│   │   ├── dynamicconfig.yaml
│   │   └── persistence.yaml
│   ├── lib/
│   └── logs/
└── kestra/
    ├── bin/
    ├── conf/application.yml
    ├── lib/
    └── logs/
```

## Temporal — 분산 워크플로우

| 항목     | 값                                              |
| ------ | ---------------------------------------------- |
| 버전     | 1.20+                                          |
| 포트     | 7233 (frontend gRPC), 8233 (Web UI HTTP)       |
| 인증     | mTLS 또는 username/password (`PP_TEMPORAL_USER`) |
| 백엔드 DB | PostgreSQL `temporal` DB                       |

### Web UI

```
http://[HOST]:8233/
```

* 워크플로우 상태 모니터링
* 실패한 워크플로우 재시도
* 액티비티 상세 보기

### 핵심 설정 (`temporal/conf/server.yaml`)

```yaml
persistence:
  defaultStore: default
  visibilityStore: visibility
  datastores:
    default:
      sql:
        pluginName: postgres12
        databaseName: temporal
        connectAddr: ${PP_POSTGRES_HOST}:${PP_POSTGRES_PORT}
        user: ${PP_TEMPORAL_USER}
        password: ${PP_TEMPORAL_PASSWORD}
    visibility:
      sql:
        pluginName: postgres12
        databaseName: temporal_visibility
        ...

global:
  membership:
    maxJoinDuration: 30s
  pprof:
    port: 7936

services:
  frontend:
    rpc:
      grpcPort: 7233
  matching:
    rpc:
      grpcPort: 7235
  history:
    rpc:
      grpcPort: 7234
  worker:
    rpc:
      grpcPort: 7239
```

### CLI

```bash
# tctl 또는 temporal CLI 사용
temporal --address localhost:7233 namespace list
temporal --address localhost:7233 workflow list
temporal --address localhost:7233 workflow describe -w <workflow_id>
```

## Kestra — 데이터 파이프라인

| 항목     | 값                                                                        |
| ------ | ------------------------------------------------------------------------ |
| 포트     | 8380 (HTTP UI + API)                                                     |
| 인증     | username/password (`PP_KESTRA_ADMIN_EMAIL` / `PP_KESTRA_ADMIN_PASSWORD`) |
| 백엔드 DB | PostgreSQL `kestra` DB                                                   |

### Web UI

```
http://[HOST]:8380/
```

기본 계정: `admin@plantpulse.io` / `Kopens123!`

### 핵심 설정 (`kestra/conf/application.yml`)

```yaml
kestra:
  server:
    port: 8380
    basic-auth:
      enabled: true
      username: ${PP_KESTRA_ADMIN_EMAIL}
      password: ${PP_KESTRA_ADMIN_PASSWORD}
  storage:
    type: local
    local:
      base-path: /data1/pp-data/kestra/storage
  queue:
    type: postgres
  repository:
    type: postgres
  datasources:
    postgres:
      url: jdbc:postgresql://${PP_POSTGRES_HOST}:${PP_POSTGRES_PORT}/kestra
      username: ${PP_KESTRA_DB_USER}
      password: ${PP_KESTRA_DB_PASSWORD}
      dialect: POSTGRES
```

### 예제 Flow

```yaml
id: daily-archive
namespace: plantpulse
description: 매일 새벽 2시에 시계열 데이터를 S3 Iceberg 로 아카이빙

triggers:
  - id: every-2am
    type: io.kestra.core.models.triggers.types.Schedule
    cron: "0 2 * * *"

tasks:
  - id: archive
    type: io.kestra.plugin.scripts.shell.Commands
    commands:
      - /opt/kopens/plantpulse-platform/plantpulse-warehouse/s3/archive.sh
```

## 운영 명령

```bash
cd /opt/kopens/plantpulse-platform/plantpulse-startup

./restart-workflow.sh      # Temporal + Kestra 함께 재시작
./status.sh                # 7233 / 8233 / 8380 RUNNING 확인
```

## 자주 발생하는 문제

| 증상             | 원인             | 조치                                          |
| -------------- | -------------- | ------------------------------------------- |
| Temporal 시작 실패 | PostgreSQL 미준비 | `temporal` / `temporal_visibility` DB 생성 확인 |
| Kestra Job 멈춤  | 워커 / 큐 적체      | Kestra Web UI 에서 worker 상태 확인               |
| Web UI 접근 안 됨  | 포트 충돌          | `ss -tlnp` 로 8233 / 8380 점유 확인              |
| 워크플로우 영구 실패    | 잘못된 코드 / 외부 의존 | 워크플로우 retry policy + compensation 설계        |

## 관련 문서

* [모듈 인덱스](/plantpulse-platform/admin/modules.md)
* [스케줄러 아키텍처 (개발자)](/plantpulse-platform/developer/scheduler.md)
* [엔진 라이프사이클 (개발자)](/plantpulse-platform/developer/engine-lifecycle.md)
