Files
pipeline/docs/EDGE_SERVER_STRUCTURE.md
T
chpark 4c1dc4082e
Build and Push Images / build-and-push (push) Has been cancelled
feat: Fleet/Collector/엣지 배포 관련 누적 작업 일괄 커밋
이전 세션들에서 작업된 아래 범위를 모두 포함:

Fleet 서브시스템 (src/fleet/)
- fleetDeviceService / fleetCommandService / fleetDeploymentService / fleetReleaseService
- fleetMetricsService, fleetScriptService, fleetEdgeConfigService
- Edge 디바이스 관리, 커맨드 발행, 배포/릴리스, 스크립트 동기화

Collector 확장
- centralMqttForwarder / centralForwarderConfigService
- equipmentStateService, pythonHookRunner, scriptCache
- Modbus/OPC-UA/S7/XGT 프로토콜 클라이언트
- targetDbIntrospection (저장 DB 조회)

Routes / API
- automationDashboardRoutes, centralForwarderRoutes, equipmentStateRoutes

DB
- importEdgeConfig (Python cached config → Pipeline DB)
- seedDataSources (external_db_connections 초기 시드)

엣지 배포 리소스
- docker/edge/Dockerfile.backend.prod, Dockerfile.frontend.prod
- docker/edge/docker-compose.edge.yml

프론트엔드
- admin/automaticMng (centralForwarder, dashboard, equipmentState)
- admin/fleet (commands, devices, deployments, releases, scripts, alerts)
- admin/pipeline-device 개선 (저장 DB 드롭다운, 태그 매핑 등)
- ExternalDbConnectionModal, ScriptsManagerDialog 등 신규 컴포넌트
- lib/api: automationDashboard, centralForwarder, equipmentState, fleet

docs/
- EDGE_SERVER_STRUCTURE, FLEET_COMPLETE, FLEET_EDGE_INTEGRATION, FLEET_HOOK_INTEGRATION

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 20:00:06 +09:00

22 KiB

엣지(스피폭스) ↔ IDC 중앙 수집 파이프라인 — 기존 기능 전수 조사 및 파이프라인 이식 가이드

조사 대상

  • 엣지 서버(고객사 수집서버): 112.168.212.142waceserver (Ubuntu, Docker Compose)
  • IDC 중앙 서버: 211.115.91.170waceserver01 (Ubuntu, Kubernetes v1.28 single-node) 조사 일자: 2026-04-20 목적: 현재 엣지+IDC가 운용 중인 "수집 → 전송 → 적재 → 조회" 전 기능을 **Pipeline 애플리케이션(vexplor_Pipeline)**에 이식하기 위한 스펙 정리

0. TL;DR — 파이프라인에 넣어야 할 기능 한 줄 요약

# 기능 현재 위치 파이프라인 이식 방식
1 다중 프로토콜 수집 (XGT/Modbus/OPC UA/S7/MQTT/SQL/CAS) 엣지 data-collector (Python) Pipeline Backend 내부 collectors/ 모듈로 이식
2 Bootstrap(MAC→UUID) / Config 원격 동기화 엣지 data-collector/bootstrap/ Pipeline 측 /api/edge/provision, /api/edge/config 제공
3 Store & Forward (로컬 Kafka 버퍼 + RetryQueue) 엣지 Kafka + publishers/retry_queue.py Pipeline 내부 큐(Kafka or Redis Streams) + 재시도 정책
4 Kafka → 중앙 MQTT 배치 포워딩 엣지 kafka-to-central-mqtt (Python, stateless) Pipeline services/forwarder/ 서비스로 이식
5 MQTT 공유구독 → TimescaleDB 배치 INSERT IDC digital-twin-web-backend Node.js (mqtt-ingestion.service.js) Pipeline Backend의 데이터 소스(TimescaleDB) 뒤단에 동일 ingestion 서비스
6 Fleet Agent 원격 관리(컨테이너 제어/헬스/오프라인큐) 엣지 fleet-agent (Node.js, device-supervisor) Pipeline이 Fleet API(fleet-api.vexplor.com) 소비 측으로 통합
7 이미지 자동 배포 체인 Harbor → Watchtower 5분 폴링 → 라벨 기반 교체 Pipeline CI/CD에서 Harbor push + 라벨 규약 유지
8 설비 상태 동기화 (개별 device_id별) IDC 백엔드 equipment-status-sync.service.js Pipeline의 equipmentStatus 실시간 갱신 모듈

2026-04-20 파이프라인 작업자 발언 (정책 결정):

"그 엣지 코드 변경되서 커밋하면 harbor에 이미지 올라가는데 플릿 에이전트가 주기적으로 harbor에 있는 이미지가 최신값인지 확인해서 변경사항이 있으면 엣지서버 최신화 될거에요"

⚠️ 사실 보정: 실제로 Harbor 폴링을 하는 주체는 Fleet Agent가 아니라 Watchtower 컨테이너입니다 (5분 간격, com.centurylinklabs.watchtower.enable=true 라벨 기준). Fleet Agent는 원격 제어/상태 보고만 담당. 파이프라인에 이식할 때 이 부분을 혼동하지 않도록 구분해야 합니다.


1. 엣지(스피폭스) 서버 — 현재 구성

1.1 전체 구성

  • OS: Ubuntu, Linux 6.8.0-110-generic
  • 오케스트레이션: Docker Compose 전용 (kubectl/kubeadm 바이너리는 있지만 클러스터는 10.10.0.74:6443 연결 거부로 꺼져 있음)
  • 이미지 소스: harbor.wace.me/vexplor_fleet/*
  • 자동 업데이트: Watchtower 컨테이너 (nickfedor/watchtower:latest, 5분 폴링, 라벨 기반)

1.2 기동 중인 컨테이너 (docker ps 시점)

컨테이너 이미지 역할
data-collector harbor.wace.me/vexplor_fleet/data-collector:latest 메인 수집기 (XGT/Modbus/OPC UA/S7/MQTT/SQL/CAS)
data-collector-alpet 동일 알펫 전용 (MSSQL, network_mode: host, EDGE_ID=ALPET-001)
fleet-agent harbor.wace.me/vexplor_fleet/device-supervisor:latest 원격 관리/헬스/컨테이너 제어
kafka-to-central-mqtt harbor.wace.me/vexplor_fleet/kafka-to-central-mqtt:latest 로컬 Kafka → 중앙 MQTT 포워더
watchtower nickfedor/watchtower:latest Harbor 폴링 자동 배포
kafka confluentinc/cp-kafka:7.5.0 (KRaft) 로컬 Store & Forward 버퍼

timescaledb, kafka-to-timescale, emqx는 통합 compose에 정의만 존재. 현재 미기동 — TimescaleDB는 IDC로 이전됨.

1.3 Data Collector 내부 (이식 대상 핵심)

컨테이너 내부 경로: /app/src/data_collector/, 엔트리 python -m data_collector.main

data_collector/
├── main.py                        # EdgeAgent 메인 루프 (bootstrap → config sync → collect → publish)
├── models.py                      # DeviceData, TagValue
├── bootstrap/
│   ├── aas_client.py              # AAS(Asset Admin Shell) API 클라이언트
│   ├── bootstrapper.py            # MAC → UUID 프로비저닝
│   └── config_syncer.py           # 서버 Config 주기 pull (기본 5분)
├── collectors/
│   ├── base.py / manager.py
│   ├── cas_collector.py / cas_protocol.py
│   ├── modbus_collector.py
│   ├── mqtt_collector.py
│   ├── opcua_collector.py
│   ├── s7_collector.py            # Siemens S7
│   ├── sql_collector.py           # MSSQL 등
│   ├── xgt_collector.py + xgt_connection_pool.py   # LS XGT
├── processors/
│   ├── aggregator.py / converter.py / filter.py
├── publishers/
│   ├── kafka_publisher.py         # 로컬 Kafka publish
│   └── retry_queue.py             # Store & Forward (max 100,000건)
├── consumers/
│   └── kafka_to_central_mqtt.py   # (임베디드 포워더 변형 — 실행은 별도 컨테이너에서)
└── config/
    └── settings.py

EdgeAgent 책임 (main.py):

  1. Bootstrap — MAC 주소로 VEX Flow 서버(https://collectormanager.vexplor.com)에서 UUID 발급
  2. Config SyncEDGE_CONFIG_SOURCE=api | aas 모드로 주기 pull
  3. Collector Manager — 태그/프로토콜별 Collector 기동
  4. Kafka Publish — 수집→edge-raw-data 토픽, 실패시 RetryQueue
  5. 변경 감지_last_values로 중복 송신 억제

실제 운용 환경변수 (스피폭스):

EDGE_SERVER_URL=https://collectormanager.vexplor.com
EDGE_CONFIG_SOURCE=api
EDGE_KAFKA_BROKERS=kafka:9092
EDGE_MQTT_BROKER_URL=mqtt://emqx:1883   # 로컬 EMQX (현재 미기동)
EDGE_MQTT_ENABLED=true
DEVICE_ID=edge-0f4d04ed
COMPANY_ID=7f5c058c-ef65-45e3-838e-cebaec2d6170   # spifox

1.4 Fleet Agent (device-supervisor) 내부

언어/구성: Node.js + TypeScript 빌드 산출물, 패키지명 device-supervisor v1.0.2

/app/dist/
├── index.js                # 엔트리
├── docker.js               # dockerode 기반 컨테이너 제어 (/var/run/docker.sock:ro 마운트)
├── heartbeat.js            # 주기 하트비트 (HEARTBEAT_INTERVAL=30)
├── metrics.js              # systeminformation 기반 시스템 지표
├── mqtt.js                 # 중앙 MQTT/Fleet API 통신
├── offline/
│   ├── store.js            # better-sqlite3 오프라인 큐
│   └── sync.js             # 복구 시 재전송
└── config.js

주요 의존성: dockerode, mqtt, systeminformation, node-cron, better-sqlite3, winston, axios 엔드포인트: FLEET_API_URL=https://fleet-api.vexplor.com, MQTT mqtt://211.115.91.170:31883 관리 대상: MANAGED_CONTAINERS=data-collector,kafka 등 (env로 주입)

역할 명확화 (⚠️ 전 담당자 발언 보정): Fleet Agent는 원격 제어/상태 보고/오프라인 큐 담당. Harbor 폴링/이미지 교체는 Watchtower가 수행하며 Fleet Agent와 무관.

1.5 Kafka → 중앙 MQTT 포워더 (Stateless Multi-Tenant)

엔트리: python -u /app/forwarder.py 토픽 규칙:

  • 데이터: dt/v1/data/{company_id}/{edge_id}
  • 하트비트: dt/v1/status/{company_id}/{edge_id}
  • QoS 1, MQTTv5
  • 배치: BATCH_SIZE=50 또는 BATCH_TIMEOUT_MS=3000

설계 포인트:

  • Stateless: 메시지 페이로드의 edge_id로 토픽 동적 라우팅 → 하나의 포워더가 다수 Edge 처리 가능
  • Config API 지원 (선택): CONFIG_API_URL이 있으면 CCM/DT Config API에서 central_mqtt.{host,port,username,password} 덮어씀
  • edge_stats로 edge_id별 forwarded/failed/first_seen/last_seen 통계 추적

Edge → 중앙 최종 MQTT 페이로드:

{
  "timestamp": "2026-04-11 11:20:14.922601",
  "edge_id": "aff81fbf-9b4c-43e0-9395-566bf47c3f9c",
  "device_id": "75570e41-821c-4813-a212-1131fc6fb538",
  "tags": { "태그명1": value, "태그명2": value },
  "priority": 2,
  "company_id": "spifox",
  "forwarded_at": "..."
}

(실 Kafka 메시지엔 plc_state, error_message 같은 부가 필드 존재)

1.6 Watchtower 자동 배포

  • 컨테이너가 5분(--interval 300)마다 Harbor 폴링
  • WATCHTOWER_LABEL_ENABLE=true — 라벨 com.centurylinklabs.watchtower.enable=true가 붙은 컨테이너만 교체
  • WATCHTOWER_CLEANUP=true — 구 이미지 자동 삭제
  • ~/.docker/config.json 마운트 → Harbor 인증 사용

라벨 정책:

  • ON (자동 업데이트): data-collector, data-collector-alpet, fleet-agent, kafka-to-central-mqtt, kafka-to-timescale
  • OFF (보수적): kafka, timescaledb, watchtower 자신

2. IDC 중앙 서버 — 현재 구성

2.1 전체 구성

  • OS: Ubuntu, Linux 6.8.0-101-generic
  • 오케스트레이션: Kubernetes v1.28.0 single-node (control-plane = waceserver01, flannel CNI)
  • 네임스페이스: digital-twin, fleet, ingress-nginx, logic-studio, wace-business-management
  • 이미지 레지스트리: 192.168.1.100:5001/digital-twin/* (내부 Harbor 프록시)

2.2 digital-twin 네임스페이스 핵심 파드

Pod 역할
digital-twin-mqtt-* EMQX 브로커 (Edge에서 들어오는 원격 MQTT)
digital-twin-timescale-0 TimescaleDB (edge_telemetry DB, 시계열 적재)
digital-twin-web-backend MQTT 구독 + TimescaleDB 적재 + API 서버 (Node.js, Express)
digital-twin-web-frontend 웹 UI (2 replicas)
digital-twin-web-postgres-0 메타데이터 PostgreSQL
digital-twin-web-redis 세션/캐시
basyx-* Eclipse BaSyx AAS 스택 (aas-discovery/env/registry, submodel-registry, cd-repository, web-ui, mongodb)
unity-webgl-server Unity 3D 뷰어
vexspace-postgres-0 Vex Space 전용 Postgres

2.3 NodePort 외부 노출 (211.115.91.170:*)

서비스 NodePort 내부 포트 용도
digital-twin-mqtt-external 31883 1883 (MQTT) Edge → 중앙 MQTT 인입
digital-twin-mqtt-external 31084 8083 (WS) MQTT WebSocket
digital-twin-mqtt-external 31183 18083 EMQX Dashboard
digital-twin-timescale-external 30543 5432 TimescaleDB 직접 조회 (파이프라인이 붙는 곳)
digital-twin-web-postgres-external 30533 5432 메타 Postgres
vexspace-postgres-external 31141 5432 Vex Space DB
fleet-emqx 31884 1883 Fleet 네임스페이스 별도 MQTT
fleet-postgres 31985 5432 Fleet 메타 DB
ingress-nginx-controller 31878/30361/31591 80/443/1884 공용 ingress (1884는 MQTT over ingress)

프론트엔드의 "데이터 소스 - PLC_탑씰"(211.115.91.170:30543 / edge_telemetry / telemetry_user)이 바로 digital-twin-timescale-external입니다.

2.4 MQTT → TimescaleDB 적재 로직 (핵심, 이식 대상)

위치: digital-twin-web-backend 컨테이너 내 src/services/ingestion/mqtt-ingestion.service.js 언어/스택: Node.js, mqtt 5.14, pg 8.17, sequelize 6.35 (단, ingestion은 생 pg Pool 사용)

EMQX 접속:

MQTT_BROKER_URL=mqtt://digital-twin-mqtt:1883
MQTT_INGESTION_USER=ingestion
MQTT_INGESTION_PASSWORD=ingestion_secret     # ⚠️ 외부용은 ingestion_secret_prod (엣지 .env 기준)

TimescaleDB 접속 (envVar):

TIMESCALE_HOST=digital-twin-timescale
TIMESCALE_PORT=5432
TIMESCALE_DB=edge_telemetry
TIMESCALE_USER=telemetry_user
TIMESCALE_PASSWORD=***MASKED***

구독 패턴 (공유구독 — 수평 확장 가능):

$share/ingestion-group/dt/v1/data/+/+
$share/ingestion-group/dt/v1/status/+/+
  • $share/<group>/... EMQX 공유구독으로 여러 백엔드 replica 간 메시지 분배
  • +/+ 와일드카드로 {company_id}/{edge_id} 모두 수신 (ACL 이슈로 # 대신 +/+ 사용)

처리 흐름 (handleTelemetryData):

  1. 토픽 파싱 → [company_id, edge_id]
  2. JSON 파싱
  3. item.tags 딕셔너리면 각 태그마다 row 1건 생성:
    time, company_id, edge_id, tag_name, value(DOUBLE), quality, metadata(JSON)
    
  4. 단일 태그 형식(tag_name/value)도 지원
  5. buffer에 쌓고 BATCH_SIZE=1000 또는 FLUSH_INTERVAL=5s 도달 시 batchInsert('edge_telemetry', rows, cols)
  6. Status(하트비트)는 edge_status 테이블에 적재 (status, ip_address, firmware_version, metadata)

신뢰성 기능:

  • Circuit Breaker: 연속 실패 5회(CIRCUIT_BREAKER_MAX_FAILURES=5) 시 OPEN, 60초 후 HALF_OPEN 회복
  • Exponential backoff 재연결 (1s → 60s)
  • 버퍼 오버플로우 방지: MAX_BUFFER_SIZE=100,000 초과 시 오래된 80%부터 drop
  • 재시도 큐: 실패 배치 최대 5,000건 재주입 (MAX_RETRY_BUFFER_SIZE=10,000)
  • stats 노출: messagesReceived/telemetryInserted/statusInserted/errors/droppedMessages/circuitBreakerTrips

설비 상태 동기화 (handleEquipmentDataReceived):

  • 메시지 내 device_id별로 원본 값(문자열 포함) 보존
  • 별도 서비스 equipment-status-sync.service.js가 개별 설비 UUID로 조회해 마지막 수신 시각/값 갱신 (Heartbeat도 포함)

2.5 TimescaleDB 스키마 (추정 + 기존 코드 근거)

timescale.config.jsbatchInsert 호출 컬럼과 과거 kafka_to_timescale.py INSERT를 조합하면 다음 형태:

edge_telemetry (hypertable 가능성, time 기준):

컬럼 타입 설명
time TIMESTAMPTZ 수집 시각
company_id TEXT/UUID 고객사 ID
edge_id TEXT 엣지 장치 ID
tag_name TEXT 태그명
value DOUBLE PRECISION 수치값 (비수치는 NULL)
quality TEXT good 기본
metadata JSONB {device_id, priority, forwarded_at, ...}

edge_status:

컬럼 타입
time, company_id, edge_id 공통
status TEXT (online 기본)
ip_address, firmware_version TEXT
metadata JSONB

실제 \d+ 확인은 digital-twin-timescale-0 파드의 psql 비밀번호가 로컬 환경에서 필요 (envVar TIMESCALE_PASSWORD) — 다음 접속 시 실 스키마/인덱스/리텐션 정책/연속집계(continuous aggregate) 확인 필요.


3. 전체 데이터 흐름

[현장 PLC/장비 — 스피폭스 공장]
  │ (XGT / Modbus / OPC UA / S7 / MQTT / MSSQL / CAS)
  ▼
[엣지 서버: data-collector 컨테이너]
  · bootstrap (MAC→UUID)
  · config sync (5분마다 collectormanager.vexplor.com)
  · 프로토콜별 Collector → processors(filter/aggregate/convert) → publish
  ▼
[로컬 Kafka — edge-raw-data 토픽] ◀─── RetryQueue (실패 재시도, 최대 10만건)
  ▼
[kafka-to-central-mqtt 포워더]
  · batch 50건 / 3초
  · 토픽 동적 라우팅: dt/v1/data/{company_id}/{edge_id}
  · QoS 1, MQTTv5
  ▼ (인터넷 경유)
═══════════════════════════════════════════════════════════════
[IDC 중앙: 211.115.91.170 K8s]
  ▼
[EMQX (digital-twin-mqtt, NodePort 31883)]
  · user=ingestion / pass=ingestion_secret_prod
  ▼ (공유구독 $share/ingestion-group/dt/v1/+/+/+)
[digital-twin-web-backend: mqtt-ingestion.service.js]
  · buffer 1000건 / 5초 flush
  · Circuit Breaker, Exponential backoff, 버퍼오버플로 방지
  · device_id별 → equipment-status-sync.service
  ▼ pg.batchInsert (ON CONFLICT DO NOTHING)
[TimescaleDB: edge_telemetry DB]
  · edge_telemetry (시계열)
  · edge_status (하트비트)
  ▲ NodePort 30543
  │
[Pipeline Frontend — 데이터 소스 "PLC_탑씰"] ← 현재 조회용 read 연결

[Fleet 관리 루프]
  fleet-agent(엣지) ──MQTT/HTTPS── fleet-api.vexplor.com ── fleet-emqx(IDC)
       │
       └─ dockerode → 엣지 컨테이너 start/stop/restart

[자동 배포 루프]
  Harbor(harbor.wace.me) ◀──push── 엣지 코드 CI
       ▲
       │ 5분 폴링 (Watchtower, label=enable)
  Watchtower(엣지) ── docker pull & recreate ──▶ 대상 컨테이너 교체

4. Pipeline 애플리케이션에 이식해야 할 기능 (작업 체크리스트)

4.1 백엔드 (backend-node)

  • /api/datasource/timescale — TimescaleDB 커넥션 풀 (pg) 추가
    • envVar: TIMESCALE_HOST/PORT/DB/USER/PASSWORD (기본 211.115.91.170:30543 / edge_telemetry / telemetry_user)
    • timescale.config.jsbatchInsert(table, rows, columns) 패턴 그대로 포팅 (ON CONFLICT DO NOTHING)
  • services/ingestion/mqtt-ingestion.service — EMQX 공유구독 + 버퍼 + Circuit Breaker
    • 토픽: $share/<groupId>/dt/v1/data/+/+, dt/v1/status/+/+
    • envVar: MQTT_BROKER_URL, MQTT_INGESTION_USER/PASSWORD, INGESTION_BATCH_SIZE=1000, INGESTION_FLUSH_INTERVAL=5000, INGESTION_MAX_BUFFER_SIZE=100000, CIRCUIT_BREAKER_MAX_FAILURES=5, CIRCUIT_BREAKER_RESET_MS=60000
    • edge_telemetry / edge_status 2개 테이블 적재 분기
  • services/forwarder/kafka-to-mqtt.service — (엣지 수집을 파이프라인이 직접 도맡을 경우) 기존 Python kafka_to_central_mqtt.py를 Node로 포팅
  • services/collectors/* — 프로토콜별 수집기 (XGT/Modbus/OPC UA/S7/MQTT/SQL/CAS) Node 이식
    • 라이브러리 후보: modbus-serial, node-opcua, nodes7, mqtt, mssql/mysql2/pg, ls-electric-xgt(자체 구현 필요)
  • services/bootstrap/provisioning — 엣지의 bootstrap/aas_client.py + bootstrapper.py 역할
    • POST /api/edge/provision으로 {mac_address, company_id} 받아 UUID/access_token 발급
    • GET /api/edge/config?edge_id=...로 수집 태그/주기 Config 반환 (기존 config_syncer.py 호환)
  • services/equipment-status-syncdevice_id별 마지막 수신시각/값 갱신
  • services/fleet-agent-bridge — Fleet API 소비자
    • 엣지에서 올라오는 heartbeat/metrics를 UI에 노출
    • 파이프라인 자체를 Fleet 피관리 대상으로도 등록 가능하게 (원격 재시작 허용)

4.2 프론트엔드 (frontend)

  • 데이터 소스 관리 화면(frontend/app/(main)/admin/automaticMng/batchmngList/)에 TimescaleDB 타입 추가 (현재는 MariaDB/PostgreSQL만)
  • 엣지 디바이스 목록(Fleet 연동) 화면 — DEVICE_ID/COMPANY_ID/last_seen/image_version 노출
  • Ingestion 실시간 통계 대시보드 — messagesReceived/telemetryInserted/droppedMessages/circuitBreakerTrips
  • 태그별 시계열 조회 — edge_telemetry 쿼리 (time_bucket, continuous aggregate 활용)

4.3 CI/CD / 배포

  • Harbor 푸시 파이프라인 — 엣지 컴포넌트(data-collector, fleet-agent, kafka-to-central-mqtt) 이미지 빌드/푸시 단계를 Jenkinsfile에 통합
  • Watchtower 라벨 정책 유지 — 새 컨테이너는 반드시 com.centurylinklabs.watchtower.enable=true 라벨을 명시적으로 붙이거나 떼기 (불투명한 자동 롤아웃 방지)
  • 릴리스 게이트:latest 즉시 롤아웃을 피할 필요가 있으면 :stable/:canary 태그 도입 검토

4.4 보안/비밀 관리

  • TimescaleDB 비밀번호, MQTT ingestion 계정, Harbor 자격, Fleet API 토큰은 K8s Secret / .env 중 한 곳에서만 관리하고 소스 커밋 금지
  • 현재 IDC digital-twin-web-backend Deployment에 평문으로 TIMESCALE_PASSWORD 노출 중 → 파이프라인 이식 시 secretKeyRef로 전환 권장

5. 외부 엔드포인트 레퍼런스

대상 주소 용도
VEX Flow (프로비저닝/Config) https://collectormanager.vexplor.com data-collector EDGE_SERVER_URL
Fleet Manager API https://fleet-api.vexplor.com fleet-agent 원격관리
중앙 MQTT (EMQX) 211.115.91.170:31883 → svc digital-twin-mqtt 엣지 → 중앙 데이터 인입
중앙 TimescaleDB 211.115.91.170:30543 → svc digital-twin-timescale 시계열 조회/적재
Harbor 레지스트리 harbor.wace.me 모든 엣지 이미지 소스
내부 Harbor 프록시(IDC) 192.168.1.100:5001 K8s 이미지 풀 경로

6. 추후 확인 필요 사항 (다음 접속 시)

  1. TimescaleDB 실제 스키마\d+ edge_telemetry, \d+ edge_status, hypertable 여부, continuous aggregate, retention policy
  2. equipment-status-sync.service.js 전체 소스 — 개별 설비 매칭 로직(equipmentId vs edgeDeviceId fallback)
  3. Fleet Manager API 엔드포인트 계약device-supervisormqtt.js/heartbeat.js의 호출 패턴
  4. EMQX ACL 설정ingestion 계정이 어떤 토픽에 write/read 권한 갖는지 (로그에서 # 구독은 거부 확인됨)
  5. Harbor repository 목록vexplor_fleet/*, digital-twin/* 태깅 규약
  6. Watchtower 라벨 전수 목록 — 각 엣지별로 어떤 컨테이너가 자동배포 대상인지 확정
  7. 백엔드 run-migration init container — TimescaleDB 마이그레이션 스크립트(/app/migrations 또는 /app/scripts) 확인하면 정확한 스키마 확보 가능

7. 관련 기존 문서