# Xisom Edge AI Box & Runtime > 실시간 예측 분석, 이상 감지, 운영 최적화를 위한 산업용 엣지 AI 플랫폼. > Full documentation corpus. Source: https://xisom-docs.pages.dev/ko/ --- ## 제품 개요 > Xisom Edge AI Box 및 런타임이 무엇이며, 누구를 위한 것이고, 산업용 스택에 어떻게 들어맞는지 설명합니다. **Xisom Edge AI Box**는 클라우드 왕복 없이 공장 현장에 실시간 AI 추론을 제공하는 산업용 엣지 어플라이언스이자 런타임입니다. ### 핵심 기능 - **실시간 추론** — 하드웨어에 맞춰 TensorRT, CUDA 또는 CPU 실행으로 ONNX 모델을 구동합니다. - **스트리밍 수집** — OPC-UA, MQTT, CSV 입력 소스를 연결합니다. - **출력 라이트백** — 예측값과 설정값을 PLC, 브로커, 파일로 전송합니다. - **모델 수명 주기** — 업로드하고, 데이터소스와 페어링한 뒤 활성화합니다. - **관측성** — 지연 시간, 처리량, 실행 공급자 대시보드를 기본 제공합니다. - **강화된 배포** — 에어갭 오프라인 설치, 비루트 컨테이너, RBAC, 감사 로그를 지원합니다. ### 대상 사용자 - 품질 검사 및 예지 보전을 자동화하는 **공장 엔지니어**. - AI 모델을 기존 SCADA/MES 시스템에 연결하는 **시스템 통합 업체**. - 데이터를 클라우드에 노출하지 않고 온프레미스 추론이 필요한 **OT/IT 팀**. ### 한눈에 보는 아키텍처 ```mermaid flowchart LR Sensors[PLC / Sensors] --> Gateway[Datasource Gateway] Gateway --> Runtime[Inference Runtime] Runtime --> Bus[Event Bus / SignalR] Bus --> UI[Web Dashboard] Bus --> Mes[MES / Historian] ``` ### 다음 단계 - [빠른 시작](/ko/install-deploy/quickstart/) 따라하기 (약 15분) - [오프라인 번들 설치](/ko/install-deploy/offline-bundle-install/)로 엣지 박스에 설치하기 - [활용 사례](/use-cases/) 둘러보기 --- ## 오프라인 번들 설치 > 오프라인 릴리스 번들로 에어갭 사이트에 Xisom Edge AI Box를 설치합니다 — 이미지 로드, 설치 프로그램 실행, 시크릿 설정, 스택 기동, 헬스 검증까지. 이것은 **인터넷, 레지스트리 접근, 박스에서의 이미지 빌드가 모두 불필요한** 상태로 고객 엣지 사이트에 설치하는 권장 방법입니다. USB 미디어로 제공되는 단일 자가 완결형 **릴리스 번들**에서 모든 것을 설치합니다. **이 페이지를 사용해야 하는 경우** 공장이나 OT 사이트의 프로덕션 엣지 박스에는 오프라인 번들을 사용하세요. 인터넷이 있는 노트북에서 빠르게 평가하려면 [빠른 시작](/ko/install-deploy/quickstart/)이 더 빠릅니다. ### 받게 되는 것 릴리스 번들은 하드웨어 프로필과 버전으로 이름 지어진 하나의 디렉터리입니다 (예: `amd64-cpu-v1.5.0`). 하드웨어에 맞는 번들을 선택하세요: | 번들 프로필 | 박스가 다음에 해당할 때 사용 | |----------------|---------------------------| | `amd64-cpu` | x86_64 CPU, GPU 없음 — CPU 전용 추론 | | `amd64-gpu` | x86_64 CPU + NVIDIA GPU — TensorRT 가속 | | `jetson` | NVIDIA Jetson (arm64 / L4T) 엣지 모듈 | 번들 내부: - amd64-cpu-v1.5.0/ - images/ - amd64-cpu-v1.5.0.images.tar.zst 세 개의 서비스 이미지, 압축됨 - manifest.txt 이미지 태그 + 체크섬 - compose/ - docker-compose.release.yml 이미지 기반 스택 (빌드 단계 없음) - .env.template 포트 + 시크릿 자리표시자 - models/ - predictive_maintenance.onnx 데모 시드 모델 (랜덤 가중치) - install.sh - update.sh - uninstall.sh - README.txt 운영자용 한 페이지 안내서 **시드 모델은 데모용입니다** 번들에 포함된 모델은 랜덤 가중치를 가지므로 — 학습된 모델로 교체하기 전까지 예측값은 무의미합니다. 아래 설치 런북의 5단계를 참조하세요. ### 시작하기 전에 대상 박스에는 다음이 이미 설치되어 있어야 합니다(골든 OS 이미지에 함께 제공됩니다 — 설치 프로그램은 이를 **확인**하고 누락된 것이 있으면 중단합니다. 대신 설치해 주지는 않습니다): - `docker compose` v2 플러그인을 갖춘 Docker Engine 20.10 이상 - `zstd` 및 `openssl` - **NVIDIA Container Toolkit** — GPU 및 Jetson 번들에만 해당 **깨끗한 대상**에 설치하세요. 릴리스 스택은 개발 스택과 동일한 컨테이너 이름, 볼륨, 호스트 포트를 재사용합니다 — Xisom이 이미 실행 중인 박스에서 실행하면 충돌합니다. ### 설치 런북 1. ### 번들을 박스로 복사 USB 미디어에서 번들 디렉터리 전체를 대상 박스로 복사한 뒤 그 안에서 터미널을 엽니다: ```bash cd amd64-cpu-v1.5.0/ ``` 2. ### 설치 프로그램 실행 설치 프로그램은 하드웨어 프로필을 자동 감지하고, 컨테이너 이미지를 로드하며, 시크릿을 생성하고, 스택을 기동한 뒤 헬스 상태를 기다립니다. ```bash sudo ./install.sh ``` 특정 프로필을 강제하거나, 부팅 시 시작 서비스도 함께 설치하려면: ```bash sudo ./install.sh --profile amd64-cpu # 프로필 강제 지정 sudo ./install.sh --with-systemd # 부팅 시 시작도 설정 ``` 내부적으로 `install.sh`는 다음 단계를 순서대로 실행합니다: 1. **사전 점검** — Docker, GPU 툴킷(GPU/Jetson), 디스크를 확인합니다. 2. 이미지 아카이브 체크섬을 **검증**합니다(손상되거나 변조된 미디어 감지). 3. **이미지 로드** — 번들 아카이브에서 `docker load`(풀, 빌드 없음). 4. **시크릿 생성** — 박스에서 JWT 서명 시크릿과 관리자 비밀번호를 만들어 보호된 `.env`(모드 `600`)에 기록합니다. 시크릿은 번들 안에 절대 포함되지 않습니다. 5. **스택 시작** — `docker compose up -d`. 6. **헬스 게이트** — 세 서비스 모두 healthy를 보고할 때까지 기다립니다. 7. **관리자 시드** 후 **대시보드 URL + 관리자 비밀번호를 한 번만 출력**합니다. 3. ### 관리자 비밀번호 저장 **단 한 번만 출력됩니다** 설치 프로그램은 실행 종료 시 대시보드 URL과 생성된 관리자 비밀번호를 **단 한 번만** 출력합니다. 즉시 비밀번호 관리자에 복사해 두세요 — 다시 읽어볼 수 있는 곳에는 저장되지 않습니다. 4. ### 시크릿이 기록되었는지 확인 설치 프로그램이 JWT 서명 시크릿을 자동으로 생성합니다. 보통은 손댈 필요가 없지만 보호된 `.env`가 존재하는지 확인할 수 있습니다: ```bash ls -l .env # 모드 -rw------- (600) 예상 ``` 직접 값을 제공해야 하는 경우(예: 회사의 시크릿 관리 정책), 첫 `up` 전에 `.env`를 편집하고 강력한 랜덤 값을 설정하세요: ```ini # .env (자리표시자 — 강력한 랜덤 값을 생성하고 재사용하지 마세요) AIBOARD_JWT_SECRET=your-strong-random-jwt-secret ``` 5. ### 데모 모델 교체 번들에는 랜덤 가중치를 가진 데모 모델이 포함됩니다. 예측값이 의미를 갖도록 학습된 `predictive_maintenance.onnx`로 교체하세요: - **첫 설치 전:** 번들의 `models/predictive_maintenance.onnx`를 덮어쓴 뒤 `install.sh`를 실행합니다. - **설치 후:** 모델을 모델 데이터 볼륨의 `/data/models`에 넣고 추론 서비스를 재시작합니다. 나중에 대시보드에서 모델을 업로드할 수도 있습니다 — [모델 배포](/configure/models/)를 참조하세요. 6. ### 헬스 검증 세 서비스 모두 healthy인지 확인합니다: ```bash docker compose -f compose/docker-compose.release.yml ps ``` 세 컨테이너가 `healthy` 상태일 것으로 예상됩니다. 그런 다음 설치 프로그램이 출력한 대시보드 URL을 열고 관리자 계정으로 로그인하세요. **GPU 첫 실행은 느립니다** GPU 및 Jetson 프로필에서는 첫 부팅 시 TensorRT 엔진을 컴파일하는 동안 추론 서비스가 2~3분간 `unhealthy` 상태로 머물 수 있습니다. 이는 정상이므로 — 기다린 뒤 `ps`를 다시 확인하세요. ### Day-2 운영 **업데이트** 더 새로운 번들을 배포합니다. 업데이트는 먼저 데이터베이스 스냅샷을 만들어 롤백 경로를 확보합니다. ```bash sudo ./update.sh ``` **실제 운영 중인 .env를 새 번들로 가져오세요** `update.sh`는 번들 디렉터리의 `.env`를 읽습니다 — 여기에는 배포별 시크릿, 포트, 이미지 참조가 들어 있습니다. 새로 내려받은 번들에는 이것이 없으므로, 업데이트를 실행하기 전에 기존 설치본의 `.env`를 새 번들 디렉터리로 복사하세요. 그렇지 않으면 중단됩니다. 업데이트 전 스냅샷은 **데이터베이스만** 포함합니다(업로드된 모델이나 TensorRT 캐시는 제외). 모델을 롤백해야 한다면 해당 볼륨을 별도로 백업하세요. **제거** ```bash ./uninstall.sh # 스택 중지, 모든 데이터 유지 ./uninstall.sh --purge # 모델 + 데이터베이스도 삭제 (확인 프롬프트) ``` ### 문제가 발생하면 | 증상 | 의미 | |---------|---------------| | `Bundle manifest missing` | 조립된 번들이 아닌 소스 폴더에서 설치 프로그램을 실행했습니다. USB에서 복사한 번들 디렉터리를 사용하세요. | | `Architecture mismatch` | 이 박스의 CPU에 맞지 않는 번들 프로필입니다. 일치하는 프로필을 사용하세요. | | `NVIDIA toolkit not wired in` | 컨테이너 툴킷이 없는 박스에 GPU/Jetson 번들을 사용했습니다. `amd64-cpu` 번들을 사용하거나 OS 이미지의 GPU 스택을 수정하세요. | | 추론이 `unhealthy`에 멈춤 | 첫 실행 TensorRT 컴파일(GPU)에 2~3분 소요됨 — 기다리세요. 그렇지 않으면 추론 로그를 확인하세요. | | 아카이브 체크섬 불일치 | 손상되거나 변조된 USB 미디어입니다. 번들을 다시 복사한 뒤 재시도하세요. | 증상별 해결 방법은 전체 [문제 해결](/ko/troubleshooting/) 런북을 참조하세요. ### 다음 단계 - [입력 데이터소스 연결](/ko/configure/input-datasources/) — 센서 데이터를 런타임으로 스트리밍합니다. - [모델 배포](/configure/models/) — 학습된 모델을 업로드하고 활성화합니다. - [첫 추론](/ko/install-deploy/first-inference/) — 모델을 시드하고 실시간 예측값을 확인합니다. --- ## 빠른 시작 > 15분 안에 Xisom Edge AI Box에서 첫 추론을 실행합니다. 이 빠른 시작 가이드는 Docker Compose를 사용하여 노트북에서 데모 스택을 실행합니다. 엣지 하드웨어가 필요하지 않습니다. ### 사전 요구사항 - Docker 24+ 및 Docker Compose v2 - 8 GB RAM, 10 GB 여유 디스크 - (선택) TensorRT 가속용 NVIDIA GPU + 드라이버 ### 1. 데모 스택 가져오기 ```bash git clone https://github.com/xisom/edge-demo.git cd edge-demo docker compose pull ``` ### 2. 런타임 시작 ```bash docker compose up -d docker compose ps ``` `http://localhost:8080` 열기 — 웹 대시보드입니다. ### 3. 샘플 추론 전송 ```bash curl -X POST http://localhost:8080/api/inference \ -H 'Content-Type: application/json' \ -d '{"model": "demo-anomaly", "payload": {"temp": 78.4, "rpm": 1450}}' ``` 예측 결과가 반환되고 대시보드의 지연시간 타일이 업데이트되는 것을 확인할 수 있습니다. ### 4. 다음 단계 - [하드웨어 설정](/ko/install-deploy/hardware-setup/) — 실제 엣지 하드웨어에 배포 - [첫 추론](/ko/install-deploy/first-inference/) — 직접 만든 모델 로드 - [입력 데이터소스 연결](/ko/configure/input-datasources/) --- ## 하드웨어 설정 > Xisom Edge AI Box를 프로비저닝하고, 실행 공급자 모드를 선택한 뒤 네트워크에 연결합니다. 소프트웨어를 설치하기 전에 박스를 프로비저닝하고, 추론 실행 방식(GPU 대 CPU)을 선택한 뒤 공장 네트워크에 연결하세요. ### 지원 하드웨어 | 등급 | CPU | GPU | RAM | 추론 모드 | |------|-----|-----|-----|----------------| | Lite | 8코어 x86_64 | — | 16 GB | CPU 전용 | | Pro | 8코어 x86_64 | NVIDIA T4 / A2 | 32 GB | TensorRT 지원 | | Max | 16코어 x86_64 | NVIDIA L4 / A10 | 64 GB | 다중 모델 | | Edge | NVIDIA Jetson (Orin / Xavier) | 통합형 | 16–32 GB | L4T(arm64)의 TensorRT | ### 엣지 GPU 사전 요구사항 박스에 NVIDIA GPU가 있는 경우(Pro, Max 또는 Jetson 등급), Xisom 설치 전에 운영체제 이미지에 다음이 준비되어 있어야 합니다: - GPU에 호환되는 **NVIDIA 드라이버**. - 컨테이너가 GPU에 접근할 수 있도록 하는 **NVIDIA Container Toolkit**. - Jetson 모듈의 경우: 툴킷이 연결된 **L4T(arm64)** 베이스 이미지. CPU 전용(Lite) 박스에서는 위의 어떤 것도 필요하지 않습니다 — 추론이 CPU에서 실행됩니다. ### 실행 공급자 모드 추론 런타임은 하드웨어에 맞는 **실행 공급자**를 선택합니다. 설치 시 일치하는 프로필을 선택합니다([오프라인 번들 설치](/ko/install-deploy/offline-bundle-install/) 참조). **TensorRT (GPU)** **최고 성능.** TensorRT 가속과 함께 NVIDIA GPU를 사용해 가장 낮은 추론 지연 시간을 제공합니다. Pro, Max, Jetson 등급에서 사용 가능합니다. - 호스트에 NVIDIA 드라이버 + Container Toolkit이 필요합니다. - 첫 실행 시 TensorRT 엔진을 컴파일합니다 — 추론 서비스가 healthy를 보고하기 전 2~3분의 워밍업을 예상하세요. **CUDA (GPU)** **TensorRT 엔진 컴파일 없는 GPU 가속.** CUDA를 통해 GPU에서 모델을 구동합니다. 모델이 TensorRT 비호환이거나 첫 실행 컴파일 지연을 피하고 싶을 때 좋은 대안입니다. - 호스트에 NVIDIA 드라이버 + Container Toolkit이 필요합니다. **CPU** **GPU 불필요.** 추론이 전적으로 CPU에서 실행됩니다. Lite 등급 박스나 GPU를 사용할 수 없는 곳에서는 `amd64-cpu` 설치 프로필을 사용하세요. - 이식성이 가장 높지만 GPU 모드보다 지연 시간이 큽니다. 모델에 맞는 GPU 모드를 선택하세요. GPU 박스가 예기치 않게 CPU로 폴백하면, 대시보드에 활성 실행 공급자가 표시되어 이를 알아챌 수 있습니다. [모니터링](/operate/monitoring/)을 참조하세요. ### 네트워크 요구사항 - 관리(대시보드 + API)를 위한 LAN 포트 1개. - 센서와 PLC가 있는 OT 네트워크를 위한 선택적 두 번째 포트. - 사용하는 경우, 라이선스 및 업데이트 서버로의 아웃바운드 HTTPS(구성 가능한 허용 목록). 에어갭 사이트는 [오프라인 번들](/ko/install-deploy/offline-bundle-install/)로 설치하며 아웃바운드 접근이 필요 없습니다. ### 첫 부팅 1. 전원과 두 네트워크 포트를 모두 연결합니다. 2. `https://your-device-ip`로 접속합니다(박스의 관리 IP로 대체). 3. 설치 중 생성한 관리자 계정으로 로그인합니다. 4. [입력 데이터소스 연결](/ko/configure/input-datasources/)로 계속 진행합니다. ### 문제가 발생하면 - GPU 박스가 CPU로 폴백하거나 첫 부팅 시 `unhealthy`에 멈춘 경우 — [문제 해결](/ko/troubleshooting/) 런북을 참조하세요. ### 다음 단계 - [오프라인 번들로 설치](/ko/install-deploy/offline-bundle-install/) — 에어갭, USB 배송 가능 설치 런북. - [데이터소스 연결](/ko/configure/input-datasources/) — 센서 및 공정 데이터를 스트리밍합니다. --- ## 첫 추론 > 모델을 시드하고, 데이터소스를 활성화한 뒤 실시간 예측값이 대시보드에 나타나는 것을 확인합니다. 설치 후 하나의 엔드투엔드 루프를 실행하세요: **모델 시드 → 데이터소스 활성화 → 예측값 확인**. 이는 런타임이 동작하는지 확인하는 가장 빠른 방법입니다. 아래 각 단계에는 전체 세부 사항을 다루는 전용 가이드가 있습니다. 이 페이지는 정상 경로 워크스루입니다. 1. ### 로그인 설치 중 출력된 대시보드 URL을 열고 관리자 계정으로 로그인합니다. 2. ### 모델 업로드 **Models**로 이동해 ONNX 모델을 업로드합니다. **윈도우 크기**와 **피처 개수**를 기록해 두세요 — 페어링할 데이터소스와 반드시 일치해야 합니다. 형식과 버전 관리는 [모델 배포](/configure/models/)를 참조하세요. 3. ### 입력 데이터소스 추가 **Datasources → Input**으로 이동해 소스를 추가합니다 — 예를 들어 MQTT 토픽이나 OPC-UA 엔드포인트. 각 센서 태그를 모델 입력 채널에 매핑하고, **윈도우 크기**를 모델에 맞게 설정합니다. 프로토콜별 전체 워크스루는 [입력 데이터소스 연결](/ko/configure/input-datasources/)을 참조하세요. 4. ### 모델과 데이터소스 페어링 데이터소스에서 업로드한 모델을 페어링합니다. 플랫폼이 호환성 검사를 실행합니다 — `윈도우 크기 × 피처 개수`가 양쪽에서 일치해야 합니다. 실패하면 데이터소스 윈도우 크기를 조정하거나 모델을 다시 생성하세요. 5. ### 활성화 후 스트리밍 시작 데이터소스 행에서 **Enable**을 토글합니다. 활성화는 구성을 검증하고, 페어링된 모델을 로드하며, 어댑터를 연결합니다 — 하지만 아직 스트리밍을 시작하지는 **않습니다**. 추론을 시작하려면 **Start**를 클릭하세요. **Enable과 Start는 두 단계입니다** Enable은 런타임을 준비합니다(모델 로드, 소스 연결). Start는 데이터 스트림을 시작합니다. 한 번에 하나의 입력 데이터소스만 활성화할 수 있습니다 — 새 데이터소스를 활성화하면 이전 것이 비활성화됩니다. 6. ### 예측값 확인 **Dashboard**를 엽니다. 약 20초 이내(첫 샘플 윈도우가 도착하면) 예측값 카드가 실시간으로 갱신되고, 센서 카드에 실시간 측정값이 표시됩니다. ### 결과 확인 모니터링 뷰는 지연 시간(p50 / p95 / p99), 처리량, 오류율, 활성 실행 공급자를 표시합니다. KPI를 읽는 방법은 [모니터링](/operate/monitoring/)을 참조하세요. ### 문제가 발생하면 - 페어링 거부, 데이터소스 활성화 실패, 예측값 미표시 — [문제 해결](/ko/troubleshooting/) 런북을 참조하세요. ### 다음 단계 - [결과 내보내기](/ko/configure/output-datasources/) — 예측값을 PLC나 브로커로 라이트백합니다. - [런타임 모니터링](/operate/monitoring/) — 지연 시간, 처리량, 드리프트. --- ## 입력 데이터소스 연결 > 센서 및 공정 데이터를 Xisom 런타임으로 스트리밍합니다 — OPC-UA, MQTT 또는 CSV 소스를 추가하고, 태그를 매핑하고, 모델을 페어링하고, 활성화한 뒤 추론을 시작합니다. **입력 데이터소스**는 하나의 논리적 센서 데이터 소스입니다 — OPC-UA 엔드포인트, MQTT 토픽 그룹, CSV 재생 파일 등. 각 소스는 자체 연결 설정과 태그 목록을 가진 유형화된 어댑터입니다. 해당 태그를 모델의 입력 채널에 매핑한 뒤 활성화하여 추론을 시작합니다. ### 지원되는 입력 소스 - **OPC-UA** — 호환 서버의 태그를 구독합니다. - **MQTT** — 직접 운영하는 브로커, TLS 지원. - **CSV** — 파일에서 기록된 데이터를 재생합니다(테스트에 적합). - **PLC** — Modbus TCP 및 기타 산업용 프로토콜. **한 번에 하나의 입력만 활성화** 어느 순간에도 **하나**의 입력 데이터소스만 활성화할 수 있습니다. 새 소스를 활성화하면 이전 소스가 자동으로 비활성화됩니다. 이는 런타임을 단일하고 예측 가능한 스트림에 묶어 둡니다. ### 워크스루: 소스 추가 및 추론 시작 1. ### 데이터소스 페이지 열기 대시보드에서 **Datasources → Input**으로 이동한 뒤 **+ Create**를 클릭합니다. 2. ### 어댑터 유형 선택 및 연결 정보 입력 소스 유형을 선택합니다. 폼은 선택한 프로토콜에 맞춰 변경됩니다 — 엔드포인트, 자격 증명, 프로토콜별 필드를 입력하세요. 시크릿 필드(비밀번호, 토큰)는 저장 후 `***`로 마스킹됩니다. **OPC-UA** | 필드 | 예시 | |-------|---------| | Endpoint | `opc.tcp://your-opcua-host:4840/factory/line1` | | Security policy | `None` (또는 인증서를 갖춘 `Basic256Sha256` 프로필) | | Authentication | `Anonymous` (또는 사용자 이름/비밀번호) | 태그는 **NodeId**로 참조됩니다. 예: `ns=2;i=2`(숫자형) 또는 `ns=2;s=Temperature`(문자열형). **MQTT** | 필드 | 예시 | |-------|---------| | Broker URL | `mqtt://your-broker-host:1883` (TLS는 `mqtts://`) | | Topic | `factory/line1/temp` | | JSON path | `value` (메시지에서 숫자 측정값을 추출) | | Username / password | 선택 사항 (저장 시 마스킹) | **CSV** | 필드 | 예시 | |-------|---------| | File | 재생할 기록된 데이터셋 | | Columns | 채널로 공급할 센서 컬럼 | CSV는 재생 전용입니다 — 실제 공장 연결 없이 모델을 테스트하는 데 유용합니다. 3. ### 태그를 모델 채널에 매핑 스트리밍할 태그를 나열하고 각각을 번호가 매겨진 **입력 채널**(`0`, `1`, `2`, …)에 할당합니다. 각 채널은 하나의 모델 입력을 공급합니다. **윈도우 크기** — 모델이 예측당 소비하는 연속 샘플 수 — 를 페어링할 모델에 맞게 설정하세요. **채널 매핑은 완전해야 합니다** 채널당 정확히 하나의 태그를 빠짐없이 제공하세요. 채널이 매핑되지 않았거나 과도하게 채워진 경우 검증에서 활성화가 거부됩니다. 문자열 태그(예: `RUN`/`STOP` 상태)는 숫자 피처로 사용할 수 없습니다 — 채널 정의 시 제외하세요. 4. ### 저장 및 모델 페어링 데이터소스를 저장합니다. 그런 다음 **Models**에서 업로드한 모델과 페어링합니다. 플랫폼이 호환성 검사를 실행하며 — `윈도우 크기 × 피처 개수`가 양쪽에서 일치해야 하고 — 다르면 페어링을 거부합니다. 5. ### 활성화 후 시작 행에서 **Enable**을 토글합니다. 이는 구성을 검증하고, 페어링된 모델을 로드하며, 어댑터를 연결합니다. 연결 핸드셰이크(OPC-UA / MQTT / Modbus)가 여기서 일어나므로, 네트워크나 자격 증명 문제가 지금 드러나 행을 오류 메시지와 함께 **Disabled**로 되돌립니다. 활성화는 스트리밍을 자동으로 시작하지 **않습니다** — 추론을 시작하려면 **Start**를 클릭하세요. ### 먼저 연결 테스트 활성화 전에 소스에서 **Test Connection**을 사용하세요. 실시간 폼 값이 엔드포인트에 도달하는지 검증하므로, 태그를 연결하기 전에 잘못된 호스트나 자격 증명을 잡아낼 수 있습니다. ### 속도 제한 및 버퍼링 각 데이터소스에는 독립적인 속도 제한과 제한된 버퍼가 있습니다. 소스가 런타임이 소비하는 속도보다 빠르게 생성하면, 백프레셔가 대시보드에 보고되어 포화를 알아챌 수 있습니다. ### 문제가 발생하면 | 증상 | 가능한 원인 | |---------|--------------| | `model_not_paired`로 활성화 실패 | 먼저 데이터소스에 모델을 페어링하세요. | | 연결 시 활성화가 롤백됨 | 엔드포인트에 도달할 수 없거나, 자격 증명이 잘못되었거나, 방화벽 문제입니다 — **Test Connection**으로 확인하세요. | | Enabled로 표시되나 예측값 없음 | 설계상 그렇습니다 — 스트리밍을 시작하려면 **Start**를 누르세요. | | 페어링 거부 | 모델과 데이터소스 간 `윈도우 크기 × 피처 개수` 불일치. | 자세한 내용은 [문제 해결](/ko/troubleshooting/) 런북을 참조하세요. ### 다음 단계 - [결과 내보내기](/ko/configure/output-datasources/) — 예측값을 PLC, 브로커, 파일로 라이트백합니다. - [모델 배포](/configure/models/) — 모델을 업로드, 페어링, 활성화합니다. --- ## 출력 데이터소스 > 예측값과 설정값을 외부 시스템으로 라이트백합니다 — OPC-UA, MQTT 또는 Modbus 출력을 구성하고 테스트 쓰기로 검증합니다. **출력 데이터소스**는 데이터를 외부 시스템 — PLC, 브로커, 제어 애플리케이션 — 으로 내보냅니다. 입력 데이터소스와 달리 출력에는 모델이나 태그 매핑이 없습니다: JSON 페이로드를 받아 대상으로 전달합니다. **현재는 테스트 쓰기** 출력 데이터소스는 현재 **온디맨드 테스트 쓰기**를 지원합니다 — 각 쓰기가 개별적으로 전송되므로 연결과 주소 지정을 검증할 수 있습니다. 예약된 배치 프로덕션 쓰기 경로는 향후 개선 사항입니다. ### 지원되는 출력 | 프로토콜 | 기능 | |----------|------------| | OPC-UA | 노드에 쓰기(동기) | | MQTT | 토픽에 게시(구성 가능한 QoS / retain) | | Modbus TCP | 보유 레지스터 또는 코일에 쓰기 | ### 출력 구성 1. ### 출력 생성 **Datasources → Output**으로 이동해 **+ Create**를 클릭하고 프로토콜을 선택합니다. 2. ### 연결 구성 입력 각 프로토콜에는 고유한 필드가 있습니다. 시크릿 필드는 저장 후 `***`로 마스킹됩니다. **OPC-UA** ```json { "endpoint": "opc.tcp://your-plc-host:4840", "securityPolicy": "None", "authentication": "Anonymous" } ``` **MQTT** ```json { "brokerUrl": "mqtt://your-broker-host:1883", "topic": "devices/plc/setpoint", "qos": 1, "retain": false, "username": "your-username", "password": "your-password" } ``` **Modbus TCP** ```json { "host": "your-plc-host", "port": 502, "slaveId": 1, "defaultCoilAddress": 1000, "defaultRegisterAddress": 40001 } ``` 주소는 Modicon 규칙을 따릅니다(코일은 `1`부터, 보유 레지스터는 `40001`부터). 3. ### 저장 및 활성화 출력을 저장한 뒤 활성화합니다. 출력이 테스트 쓰기를 받으려면 먼저 **활성화**되어 있어야 합니다. ### 테스트 쓰기 보내기 출력의 상세 정보를 열고 **Test Write** 동작을 찾아 JSON 페이로드를 입력한 뒤 전송합니다. 결과에는 성공 또는 대상이 반환한 프로토콜 오류가 표시됩니다. **OPC-UA** 단일 노드 쓰기: ```json { "nodeId": "ns=2;s=Temperature", "value": 42.5 } ``` **MQTT** 페이로드 게시: ```json { "setpoint": 65.0, "mode": "heating" } ``` **Modbus** 하나 이상의 레지스터 쓰기: ```json { "addresses": [ { "address": 1000, "value": 100 }, { "address": 1002, "value": 250 } ] } ``` 쓰기에 성공하면 성공 결과가 반환되고, 프로토콜 오류 시 `OPC UA: node not found`나 `Modbus: address out of range` 같은 메시지가 반환됩니다. ### 문제가 발생하면 | 증상 | 가능한 원인 | |---------|--------------| | `Connection timeout` | 대상에 도달할 수 없음 — 네트워크 경로, 호스트, 포트(MQTT `1883`, Modbus `502`)를 확인하세요. | | 데이터소스 비활성화 오류 | 테스트 쓰기를 보내기 전에 출력을 활성화하세요. | | `Node not found` (OPC-UA) | NodeId 형식과 해당 노드가 서버에 존재하는지 확인하세요. | | 주소 범위 초과 (Modbus) | Modicon 범위 내로 유지하세요(코일 `1+`, 보유 레지스터 `40001+`). | | `Admin only` (403) | 테스트 쓰기에는 관리자 계정이 필요합니다. | 자세한 내용은 [문제 해결](/ko/troubleshooting/) 런북을 참조하세요. ### 다음 단계 - [입력 연결](/ko/configure/input-datasources/) — 센서 데이터를 스트리밍해 추론을 구동합니다. - [런타임 모니터링](/operate/monitoring/) — 지연 시간, 처리량, KPI. --- ## 문제 해결 > Xisom Edge AI Box의 일반적인 문제를 진단하고 해결하기 위한 증상 중심 런북입니다. 표에서 증상을 찾아 해당 해결 방법으로 이동하세요. 모든 런북 페이지는 동일한 구조를 따릅니다: **증상 → 확인 → 해결 → 예방**. ### 증상 찾기 | 다음과 같은 현상이 보이면… | 이동 | |---|---| | GPU가 설치되어 있으나 추론이 CPU에서 실행됨; 로그에 `CUDA failure 500: named symbol not found` 표시 | [GPU 미사용 — CUDA 오류 500](/ko/troubleshooting/gpu-cuda-error-500/) | | GPU 강제 적용 시 호스트 GPU가 손상되어 추론 컨테이너가 종료/재시작됨 | [GPU 미사용 — CUDA 오류 500](/ko/troubleshooting/gpu-cuda-error-500/) | | 호스트 GPU는 동작하나 공급자가 예상보다 낮은 등급으로 표시됨 (예: TensorRT 대신 CUDA, 또는 CPU) | [GPU 예상 시 CPU에서 실행됨](/ko/troubleshooting/execution-provider-fallback/) | | 로그 줄 `Failed to load library libonnxruntime_providers_tensorrt.so` | [GPU 예상 시 CPU에서 실행됨](/ko/troubleshooting/execution-provider-fallback/) | | 데이터소스 활성화가 `model_not_paired` 또는 `adapter_connect_failed`와 함께 다시 비활성화로 되돌아감 | [데이터소스 다운 또는 장애](/ko/troubleshooting/datasource-down-or-faulted/) | | 스트리밍 OPC-UA / MQTT / CSV 소스가 멈춤; 행에 장애 표시 / 빨간 배너가 나타남 | [데이터소스 다운 또는 장애](/ko/troubleshooting/datasource-down-or-faulted/) | | 데이터소스가 활성화되었으나 예측값이 나타나지 않음 | [데이터소스 다운 또는 장애](/ko/troubleshooting/datasource-down-or-faulted/) | | **설정 → 정보**에서 프런트엔드 / 백엔드 버전이 호박색으로 강조됨 | [프런트엔드 / 백엔드 버전 불일치](/ko/troubleshooting/version-drift-fe-be/) | | 버전이 `0.0.0-dev…`로 표시되거나 `-dirty`로 끝남 | [프런트엔드 / 백엔드 버전 불일치](/ko/troubleshooting/version-drift-fe-be/) | | 디스크가 가득 참; 데이터베이스 파일이 보유 데이터보다 훨씬 큼; 행을 삭제해도 줄어들지 않음 | [디스크 가득 참 / 데이터베이스 무한 증가](/ko/troubleshooting/sqlite-retention-disk/) | | `docker ps`에서 컨테이너가 `unhealthy`로 표시되나 앱은 정상 동작함 | [컨테이너가 unhealthy로 표시되나 서비스는 정상](/ko/troubleshooting/healthcheck-ipv6-false-negative/) | 배포된 스택은 릴리스 compose 파일을 사용합니다. 이 페이지들의 명령은 실행 중인 컨테이너(`aiboard-inference-real`, `aiboard-backend-real`, `aiboard-frontend-real`)에 대해 `docker compose -f docker-compose.release.yml …`로 작성되었습니다. --- ## GPU 미사용 — CUDA 오류 500 > GPU가 설치되어 있는데도 추론 컨테이너가 "CUDA failure 500"을 로깅하고 CPU로 폴백합니다. ### 증상 박스에 NVIDIA GPU가 있는데도 추론이 CPU에서 실행됩니다. 추론 컨테이너 로그에 다음이 표시됩니다: ``` CUDA failure 500: named symbol not found ... resolved to CPU only ``` 대시보드의 **Python Inference** 카드에 `TensorRT`나 `CUDA` 대신 CPU (fallback)가 표시됩니다. 엄격한 GPU 강제 적용이 켜져 있으면 컨테이너가 대신 종료되고 계속 재시작됩니다. `nvidia-smi`가 GPU를 존재하는 것으로 보고한다고 해서 CUDA 컴퓨트가 동작한다는 의미는 **아닙니다**. 관리 계층(`nvidia-smi`)과 컴퓨트 드라이버는 서로 다른 것입니다 — 여기서 실패한 것은 컴퓨트 드라이버입니다. ### 확인 결정적인 테스트는 NVIDIA의 자체 컴퓨트 샘플입니다. 호스트에서 실행하세요: ```bash docker run --rm --gpus all nvcr.io/nvidia/k8s/cuda-sample:vectoradd-cuda12.5.0 ``` - **정상 호스트** → `Test PASSED`를 출력합니다. - **손상된 호스트** → `Failed to allocate device vector A (error code named symbol not found)!` `nvidia-smi -L`로 GPU가 나열되는데도 이 샘플이 실패하면, 컨테이너에서 컴퓨트 용도로 GPU를 사용할 수 없습니다. 문제는 호스트 GPU 스택이지 Xisom 이미지가 아닙니다. 실행 중인 컨테이너 로그에서도 폴백을 확인할 수 있습니다: ```bash docker compose -f docker-compose.release.yml logs inference | grep -E "CUDA failure 500|resolved to CPU" ``` ### 해결 이것은 플랫폼 드라이버 계층의 호스트 수준 GPU 컴퓨트 문제이므로, 해결은 호스트에서 이루어집니다 — Xisom 스택이 아닙니다. 1. 호스트의 **NVIDIA GPU 드라이버**를 완전한 CUDA 컴퓨트 지원을 갖춘 최신 빌드로 업데이트합니다. Windows + WSL2 호스트에서는 이후 `wsl --update`를 실행한 뒤 `wsl --shutdown`도 실행하세요. Docker / 컨테이너 플랫폼만 업데이트해서는 이 문제가 **해결되지 않습니다**. 컴퓨트 실패는 컨테이너 런타임 아래의 GPU 드라이버 계층에 있습니다. 플랫폼 업데이트는 GPU 패스스루 설정을 초기화해 오히려 상황을 악화시킬 수도 있습니다. 2. 새로 고친 드라이버에 컨테이너 런타임을 다시 연결합니다: ```bash sudo nvidia-ctk runtime configure --runtime=docker sudo systemctl restart docker ``` 3. 컴퓨트 샘플이 `Test PASSED`를 출력할 때까지 다시 실행합니다: ```bash docker run --rm --gpus all nvcr.io/nvidia/k8s/cuda-sample:vectoradd-cuda12.5.0 ``` 4. 추론 컨테이너를 재시작하고 GPU를 인식했는지 확인합니다. 구성 변경은 필요 없습니다 — 기본 실행 모드가 사용 가능한 최적의 공급자를 자동 선택합니다: ```bash docker compose -f docker-compose.release.yml restart inference docker compose -f docker-compose.release.yml logs inference | grep "EP enabled" # 예상: "TensorRT EP enabled" 또는 "CUDA EP enabled" ``` #### 호스트가 아직 손상된 동안 GPU 프로브와 씨름하는 대신 CPU에서 깔끔하게 실행하세요. 추론 서비스에 `EXECUTION_MODE=cpu`(또는 `FORCE_CPU=1`)를 설정하고 재시작합니다. 드라이버가 수정될 때까지 박스는 CPU에서 계속 예측합니다. 손상된 호스트에서 GPU 모드와 함께 엄격한 GPU 강제 적용(`STRICT_EP=1`)을 **켜지 마세요** — 조용히 CPU로 동작하는 대신 올바르게 시작을 거부하게 되며, 이는 우회 작업 중 원하는 것과 정반대입니다. ### 예방 - **GPU가 필수인 곳에서는 조용한 CPU 폴백을 시끄럽게 만드세요.** 반드시 GPU에서 실행해야 하는 박스에서는 공급자를 고정하고 엄격한 강제 적용(`EXECUTION_MODE=cuda` + `STRICT_EP=1`)을 켜서, GPU 라이브러리가 없으면 조용히 CPU로 저하되는 대신 시작 시 컨테이너가 실패하도록 하세요. - **GPU 드라이버나 호스트 업데이트 후 컴퓨트 샘플을 합격 게이트로 취급하세요** — `nvidia-smi` 통과만으로는 추론이 GPU를 사용한다는 충분한 증거가 아닙니다. - **첫 실행은 느린 것이지 멈춘 것이 아닙니다.** 첫 GPU 시작 시 엔진이 컴파일되고 캐시됩니다(2~3분). 이후 재시작은 빠릅니다. 긴 시작 구간을 위 실패와 혼동하지 마세요. ### 관련 항목 - [GPU 예상 시 CPU에서 실행됨](/ko/troubleshooting/execution-provider-fallback/) — 호스트 GPU는 정상이나 공급자가 여전히 등급을 낮출 때. - [하드웨어 설정](/ko/install-deploy/hardware-setup/) — 지원되는 GPU 등급. - [관측성 및 알림](/operate/monitoring/) — 활성 공급자가 표시되는 곳. --- ## GPU 예상 시 CPU에서 실행됨 > GPU가 동작하는데도 추론이 더 낮은 실행 공급자(TensorRT → CUDA → CPU)로 저하됩니다. ### 증상 호스트 GPU는 동작하지만(CUDA 컴퓨트 샘플이 통과함) 추론이 예상보다 느리고 **Python Inference** 카드에 프로비저닝한 것보다 낮은 등급이 표시됩니다: - TensorRT를 예상했으나 CUDA가 보이거나, - GPU 공급자를 예상했으나 CPU (fallback) 또는 CPU가 보임. 추론 컨테이너 로그에는 흔히 다음과 같은 무해해 보이는 줄이 포함됩니다: ``` Failed to load library libonnxruntime_providers_tensorrt.so ``` 이것은 공급자 체인이 한 번에 한 등급씩 저하되는 것입니다: **TensorRT → CUDA → CPU**. 이 페이지는 호스트 GPU가 정상인데도 공급자가 등급을 낮출 때를 위한 것입니다. 컨테이너가 `CUDA failure 500`을 로깅한다면 GPU 자체가 손상된 것입니다 — 먼저 [GPU 미사용 — CUDA 오류 500](/ko/troubleshooting/gpu-cuda-error-500/)을 참조하세요. ### 확인 실행 중인 서비스가 실제로 초기화한 공급자를 확인하세요: ```bash docker compose -f docker-compose.release.yml logs inference | grep -E "Active EP|EP enabled|resolved to" ``` - `TensorRT EP enabled` → 최상위 등급, 할 일 없음. - TensorRT를 예상했는데 `CUDA EP enabled` → TensorRT 공급자가 제외됨(네이티브 파서 라이브러리 누락). - GPU가 동작하는데 `resolved to CPU only` → 두 GPU 공급자 모두 제외됨. 기본 실행 모드는 `auto`이며, 이는 실패하기보다 우아하게 저하되도록 *설계*되어 있습니다. 그 안전망이 곧 누락된 GPU 라이브러리를 숨기는 것이기도 합니다 — 저하된 박스는 더 느릴 뿐 계속 실행됩니다. ### 해결 1. 박스가 **반드시** GPU에서 실행되어야 하는지 결정합니다. GPU가 필수라면, 격차가 조용히 묻히지 않고 보이도록 폴백을 시끄럽게 만드세요 — 추론 서비스에서 공급자를 고정하고 엄격한 강제 적용을 켭니다: ``` EXECUTION_MODE=tensorrt # 또는: cuda STRICT_EP=1 ``` 추론을 재시작합니다. 이제 요청한 공급자를 사용할 수 없으면 컨테이너가 저하되는 대신 시작을 거부합니다 — 조용한 속도 저하를 조치 가능한 명백한 시작 실패로 바꿉니다. 2. 요청한 등급이 **TensorRT**인데 제외되었다면, 이미지에서 TensorRT 공급자의 네이티브 파서 라이브러리가 누락된 것입니다. 이 박스에는 CPU 프로필 대신 GPU 지원 추론 이미지 프로필(TensorRT)을 사용하세요. 재배포 전에 이미지 등급이 하드웨어 등급과 일치하는지 확인하세요. 3. 재시작 후 활성 공급자를 다시 확인합니다: ```bash docker compose -f docker-compose.release.yml restart inference docker compose -f docker-compose.release.yml logs inference | grep "EP enabled" ``` ### 예방 - **이미지 프로필을 하드웨어에 맞추세요.** 추론 이미지는 CPU, TensorRT, Jetson 프로필로 제공됩니다. GPU 박스에 CPU 프로필을 배포하면 구조상 CPU에 묶입니다 — 그 이미지에는 *폴백할* GPU 공급자가 없습니다. - **GPU 필수 박스에는 고정 + 엄격 적용.** `EXECUTION_MODE=auto`는 혼합 플릿에 적합한 기본값이지만, 반드시 GPU를 사용해야 하는 박스에서는 `EXECUTION_MODE=` + `STRICT_EP=1`이 보이지 않는 저하를 빠른 실패형 시작 오류로 바꿉니다. - **배포할 때마다 공급자 칩을 확인하세요.** 대시보드의 Python Inference 카드는 실시간 공급자를 반영합니다. 거기서 등급이 떨어지면 정상적인 변동이 아니라 배포 결함으로 취급하세요. ### 관련 항목 - [GPU 미사용 — CUDA 오류 500](/ko/troubleshooting/gpu-cuda-error-500/) — 호스트 GPU 컴퓨트 계층이 실제로 손상된 경우. - [하드웨어 설정](/ko/install-deploy/hardware-setup/) — 어떤 GPU 등급이 어떤 이미지 프로필에 매핑되는지. - [관측성 및 알림](/operate/monitoring/) — 활성 공급자와 지연 시간 읽기. --- ## 데이터소스 다운 또는 장애 > OPC-UA / MQTT / CSV 데이터소스가 스트리밍을 멈추거나, 행에 장애 표시가 나타납니다. ### 증상 다음 둘 중 하나입니다: - **활성화 실패.** 입력 데이터소스 행에서 **Enable**을 토글하면 **Disabled**로 다시 되돌아가고 `model_not_paired` 또는 `adapter_connect_failed` 같은 오류 코드가 표시됩니다. - **실행 중인 소스가 스트림 도중 장애를 일으킴.** 이전에 스트리밍하던 데이터소스가 예측값 생성을 멈추고, 행에 장애 표시와 빨간 배너가 나타납니다. 내부적으로 런타임이 장애 상태로 떨어진 뒤 유휴 상태가 됩니다. 데이터소스를 활성화해도 스트리밍이 시작되지 **않습니다**. 활성화는 런타임을 준비합니다 (모델 로드, 어댑터 연결). 추론을 시작하려면 **Start**를 누르세요. Enabled이지만 예측하지 않는 행은 대개 Start를 기다리는 것일 뿐이며 — 이는 설계상 그렇습니다. ### 확인 어느 단계에서 실패했는지 식별하세요. - **행의 오류 코드를 읽으세요.** 코드는 실패한 단계를 직접 가리킵니다: - `model_not_paired` → 데이터소스에 할당된 모델이 없음. - `adapter_connect_failed` → 프로토콜 연결(OPC-UA 핸드셰이크 / MQTT 브로커 연결 / Modbus 프로브)이 실패함 — 거의 항상 네트워크나 엔드포인트 문제. - 연결 시도와 장애 원인을 보려면 **백엔드 로그를 확인하세요**: ```bash docker compose -f docker-compose.release.yml logs backend | grep -iE "datasource|adapter|fault|enable" ``` - **스트림 도중 장애**는 실시간 소스의 읽기 오류(태그 응답 중단)에서 비롯됩니다. 그런 일이 생기면 런타임은 깨끗한 유휴 상태로 분해됩니다: 모델이 언로드되고 어댑터 바인딩이 해제됩니다. **복구는 자동이 아닙니다** — 소스를 처음부터 다시 활성화해야 합니다. ### 해결 1. **`model_not_paired`** — 먼저 데이터소스에 모델을 할당한 뒤 다시 Enable하세요. 데이터소스는 실행 전에 모델과 페어링되어 있어야 합니다. 2. **`adapter_connect_failed`** — 런타임이 소스에 도달하지 못했습니다. Enable이 이미 행을 Disabled로 깔끔하게 되돌렸으므로, 연결을 수정한 뒤 재시도하세요: - 데이터소스 구성의 엔드포인트 주소, 포트, 자격 증명을 확인하세요. - 박스가 OPC-UA 서버 / MQTT 브로커 / 장치에 네트워크로 도달할 수 있는지 확인하세요. - CSV 소스의 경우 파일 경로와 파일이 존재하고 읽을 수 있는지 확인하세요. - 폼의 **Test Connection**을 *현재* 값으로 실행한 뒤 다시 Enable하세요. 3. **스트림 도중 장애가 난 소스**는 유휴 상태로 분해되었습니다 — 모델이 언로드되고 어댑터 바인딩이 해제되었습니다. 완전히 다시 설정하세요: - 근본적인 소스 문제를 해결하세요(센서 재연결 / 태그 복구 / 네트워크 수정). - 데이터소스를 다시 **Enable**합니다(모델을 다시 로드하고 어댑터를 재연결). - **Start**를 눌러 스트리밍을 재개합니다. 한 번에 하나의 입력 데이터소스만 Enabled될 수 있습니다. 소스 B를 활성화하면 소스 A가 자동으로 비활성화됩니다. 실행 중일 것으로 예상한 소스가 Disabled로 표시되면, 다른 소스를 활성화하면서 꺼진 것은 아닌지 확인하세요. ### 예방 - **운영에 들어가기 전에 모델을 페어링하고 모든 채널을 매핑하세요.** 대부분의 활성화 실패는 사전 점검 검증입니다: 모델 페어링 누락 또는 불완전한 태그-채널 매핑(빈 곳 또는 과도 채움). 이를 한 번 완료하면 Enable이 첫 시도에 성공합니다. - **Start 전에 소스를 안정화하세요.** 불안정한 OPC-UA 엔드포인트나 간헐적인 브로커는 스트림에 장애를 일으켜 전체 재활성화를 강제합니다. Start를 누르기 전에 Test Connection으로 안정적인 연결을 확인하세요. - **두 단계 흐름을 예상하세요.** Enable은 준비하고, Start는 스트리밍합니다. 이를 운영 절차에 반영하면 "활성화되었는데 아무 일도 일어나지 않는다"는 티켓을 피할 수 있습니다. ### 관련 항목 - [데이터소스 연결](/ko/configure/input-datasources/) — 어댑터 유형, 연결 구성, 태그 매핑. - [첫 추론](/ko/install-deploy/first-inference/) — 모델 로드 및 실행 흐름. --- ## 프런트엔드 / 백엔드 버전 불일치 > 정보 패널이 프런트엔드와 백엔드 간 버전 불일치를 호박색으로 강조합니다. ### 증상 **설정 → 정보**에서 프런트엔드 버전과 백엔드 버전이 나란히 표시되며, 서로 다르기 때문에 해당 행이 **호박색**으로 강조됩니다. 예를 들어: - 프런트엔드 `v1.5.0` 대 백엔드 `v1.5.0-1-gabc1234`, 또는 - 프런트엔드 `v1.5.0` 대 백엔드 `v1.4.0`. 패널은 의도적으로 불일치를 강조합니다 — 내장된 드리프트 탐지기입니다. 올바르게 배포된 박스에서는 두 버전이 일치해야 합니다. ### 확인 백엔드가 보고하는 버전을 직접 읽어 정보 패널이 프런트엔드에 대해 표시하는 것과 비교하세요: ```bash curl -s http://localhost:5000/api/system/health | jq '{version, commit, buildTime}' ``` 흔한 원인은 두 가지입니다: 1. **오래된 브라우저 탭.** 프런트엔드 번들은 빌드 시점에 구워지므로, 재배포 후에도 오래된 탭은 로드된 버전을 계속 표시합니다. 2. **한 컨테이너만 다른 것 없이 재배포됨.** 새 백엔드 이미지는 배포되었으나 프런트엔드 이미지(또는 그 반대)는 다시 빌드되지 않아 — 실제로 서로 다른 버전을 실행합니다. 태그 뒤에 추가 커밋이 붙은 버전 문자열(`...-1-gabc1234`)은 그 컨테이너가 릴리스 태그를 *지난* 커밋에서 빌드되었음을 의미합니다 — 깨끗한 태그 빌드보다 앞서 있습니다. ### 해결 1. **먼저 오래된 탭을 배제하세요** — 가장 비용이 적은 원인입니다. 브라우저를 하드 새로고침하세요(캐시 무시 다시 로드). 이제 프런트엔드와 백엔드가 일치하면 끝입니다. 배포에는 실제로 문제가 없었던 것입니다. 2. **여전히 다르면, 두 이미지가 같은 버전이 되도록 재배포하세요.** 프런트엔드와 백엔드가 같은 릴리스에서 빌드되도록 다시 빌드하고 함께 스택을 기동합니다: ```bash docker compose -f docker-compose.release.yml up -d ``` 3. **일치하는지 검증하세요.** **설정 → 정보**를 다시 로드하면 호박색 강조가 사라져야 합니다. 백엔드 버전도 확인하세요: ```bash curl -s http://localhost:5000/api/system/health | jq .version ``` 버전이 `0.0.0-dev...`로 표시되거나 `-dirty`로 끝나는 빌드는 배포하지 마세요. 전자는 그 빌드에 릴리스가 태그되지 않았음을 의미하고, 후자는 커밋되지 않은 로컬 편집을 담고 있음을 의미합니다. 둘 다 재현 불가능합니다 — 제대로 버전이 매겨진 릴리스를 배포하세요. ### 예방 - **프런트엔드와 백엔드를 함께 배포하세요.** 두 이미지 모두 빌드 시점에 구워진 버전을 지닙니다. 하나의 릴리스에서 짝을 이룬 세트로 배포하면 정보 패널이 녹색을 유지합니다. - **배포할 때마다 하드 새로고침하세요.** 브라우저 탭은 다시 로드하기 전까지 오래된 번들을 유지합니다. 배포 후 빠른 하드 새로고침은 잘못된 드리프트 보고를 방지합니다. - **정보 패널을 배포 후 점검으로 사용하세요.** 이를 최종 합격 단계로 취급하세요 — 호박색이면 배포가 끝나지 않은 것입니다. ### 관련 항목 - [릴리스 노트](/release-notes/) — 각 버전에 포함된 내용. - [관측성 및 알림](/operate/monitoring/) — 버전과 함께 보는 런타임 헬스. --- ## 디스크 가득 참 / 데이터베이스 무한 증가 > 관측성 데이터베이스가 실제 데이터보다 훨씬 커지며 디스크를 가득 채웁니다. ### 증상 박스의 디스크가 부족해지고, 관측성 데이터베이스 파일이 보유해야 할 데이터량에 비해 거대해 집니다 — 예를 들어 실제 보존된 예측값은 약 1 GB뿐인데 디스크에서는 수십 GB를 차지합니다. 오래된 행을 삭제(또는 보존 기간 단축)해도 파일이 줄어들지 **않습니다**. 원인: 오래된 행은 일정에 따라 삭제되지만, 증분 auto-vacuum 없이 생성된 데이터베이스에서는 해제된 페이지가 내부 free-list에 보관되어 운영체제로 결코 반환되지 않습니다. 파일은 영원히 최고 수위에 머뭅니다. ### 확인 백엔드 컨테이너 내부에서 데이터베이스 파일 크기를 예상 실제 크기와 비교해 확인하세요: ```bash docker compose -f docker-compose.release.yml exec backend \ sh -c 'ls -lh /data/aiboard.db' ``` 보존 윈도우가 보유해야 할 데이터량보다 파일이 몇 배나 크다면(며칠치 예측값에 대해 수 GB 파일), free-list 비대화 상태입니다. 한 번의 고속 쓰기 버스트가 파일을 정상 상태 크기보다 훨씬 부풀릴 수 있습니다. ### 해결 현재 릴리스는 각 보존 스윕 후 공간을 자동으로 회수하므로, 정상 박스는 스스로 교정됩니다. 해당 동작이 적용되기 **전에** 비대해진 데이터베이스는 일회성 회수가 필요합니다. 1. 회수 중 데이터베이스에 쓰기가 발생하지 않도록 **백엔드를 중지**합니다: ```bash docker compose -f docker-compose.release.yml stop backend ``` 2. 데이터베이스 파일에 대해 **일회성 회수를 실행**합니다. 이는 파일을 증분 auto-vacuum으로 변환하고 압축하여 free-list 페이지를 OS로 반환합니다: ```bash docker compose -f docker-compose.release.yml run --rm --entrypoint sh backend -c \ 'sqlite3 /data/aiboard.db "PRAGMA auto_vacuum=INCREMENTAL; VACUUM;"' ``` 3. 파일이 줄었고 데이터가 온전한지 **검증**합니다: ```bash docker compose -f docker-compose.release.yml run --rm --entrypoint sh backend -c \ 'ls -lh /data/aiboard.db; sqlite3 /data/aiboard.db "PRAGMA integrity_check;"' # 훨씬 작은 파일과 다음을 예상: ok ``` 4. **백엔드를 다시 시작**합니다: ```bash docker compose -f docker-compose.release.yml start backend ``` 일회성 회수는 자동 회수가 적용되기 전에 커진 데이터베이스에만 필요합니다. 새로운 배포는 각 보존 스윕 후 스스로 줄어들고 압축 상태를 유지할 수 있도록 생성됩니다. ### 예방 - **보존 기간을 제한하세요.** 보존 윈도우(`InferenceObservability:RetentionDays`, 기본 3일)는 일정에 따라 오래된 예측 행을 삭제하고, 삭제 후 회수가 해제된 페이지를 디스크로 반환하여 파일을 가볍게 유지합니다. - **버스트 가드로 행 개수를 제한하세요.** `InferenceObservability:MaxRows`(기본 5,000,000)는 시간 윈도우 안에서도 최신 N개 행으로 잘라내므로, 갑작스러운 고속 급증이 시간 기반 스윕이 실행되기 전에 디스크를 채우지 못하게 합니다. 박스의 디스크가 제한적이라면 이 값을 낮추세요. - **처리량/스텁 테스트에 주의하세요.** 고속 쓰기 버스트(예: 센서 파이프라인 처리량 테스트)가 애초에 파일을 부풀리는 원인입니다. 프로덕션 박스의 영구 저장소에 대해 고속 테스트를 켜둔 채로 두지 마세요. ### 관련 항목 - [관측성 및 알림](/operate/monitoring/) — 관측성 데이터베이스가 저장하는 내용. --- ## 컨테이너가 unhealthy로 표시되나 서비스는 정상 > 컨테이너가 트래픽을 정상 처리하는데도 unhealthy로 보고됩니다 — 헬스 프로브 거짓 음성. ### 증상 컨테이너 — 보통 프런트엔드 — 가 `docker ps`에서 unhealthy로 표시되는데도, 애플리케이션은 브라우저에서 정상적으로 도달하고 서비스됩니다. 서비스가 작동 중인데도 헬스 프로브는 "connection refused"를 로깅합니다. 원인: 프로브가 `localhost`를 대상으로 하는데, 이는 IPv6(`::1`)와 IPv4(`127.0.0.1`) **모두**로 해석됩니다. 프로브 도구는 IPv6를 먼저 시도하지만, 컨테이너 내부의 서버는 IPv4에서만 수신 대기하므로 — IPv6 시도가 거부되고 프로브가 컨테이너를 다운으로 잘못 보고합니다. ### 확인 컨테이너가 보고한 헬스 상태를 실제 도달 가능성과 비교해 확인하세요: ```bash docker ps --format '{{.Names}}\t{{.Status}}' | grep aiboard ``` 컨테이너가 `(unhealthy)`로 표시되는데 IPv4로 직접 접근했을 때 앱이 응답한다면, 이 거짓 음성입니다: ```bash # 컨테이너 내부에서 — IPv4 명시 docker compose -f docker-compose.release.yml exec frontend \ sh -c 'wget -qO- http://127.0.0.1:8080/ >/dev/null && echo "IPv4 OK"' ``` `IPv4 OK`가 출력되면 서비스는 정상이고 프로브만 잘못된 것입니다. ### 해결 현재 이미지는 이미 IPv4를 명시적으로 프로빙하므로 정상 박스에서는 이 문제가 발생하지 않습니다. 이전 이미지에서 이를 본다면: 1. **서비스가 실제로 작동 중인지 확인하세요**(위의 IPv4 검사가 `IPv4 OK`를 출력). 출력되면 애플리케이션 측 조치는 필요 없습니다 — 컨테이너는 정상입니다. 2. **현재 릴리스 이미지로 업데이트하세요.** 여기서는 헬스 프로브가 `127.0.0.1`을 직접 대상으로 하여 거짓 음성이 사라집니다: ```bash docker compose -f docker-compose.release.yml up -d ``` 3. 상태가 해소되는지 **다시 확인**하세요: ```bash docker ps --format '{{.Names}}\t{{.Status}}' | grep aiboard ``` 잘못된 것은 (서비스가 아니라) 프로브이므로, 이 컨테이너의 헬스를 기다리는 의존 컨테이너가 실제로는 모두 정상 서비스 중인데도 대기 상태에 머물 수 있습니다. 헬스체크를 비활성화하지 말고 프로브를 수정하세요. ### 예방 - **서버가 실제로 바인딩하는 주소를 프로빙하세요.** 서버가 IPv4에서만 수신 대기할 때는 헬스 프로브가 `127.0.0.1`을 명시적으로 대상으로 해야, 이름 해석이 프로브를 바인딩되지 않은 IPv6 주소로 보내지 않습니다. - **프로브 실패와 서비스 실패를 구분하세요.** `unhealthy` 상태에 반응하기 전에 직접 IPv4 요청으로 확인하세요. 빨간 프로브 뒤의 녹색 앱은 장애가 아니라 프로브 버그입니다. ### 관련 항목 - [관측성 및 알림](/operate/monitoring/) — 헬스 및 상태 신호 해석. --- ## API 레퍼런스 > Xisom 런타임이 노출하는 REST 및 gRPC API. 모든 HTTP 엔드포인트는 `/api/v1/` 아래에 버전이 지정되어 있으며 베어러 토큰이 필요합니다. gRPC 서비스는 `50051` 포트에 노출됩니다. ### 인증 ```http Authorization: Bearer ``` **관리자 → API 키** 페이지 또는 `/api/v1/auth/token` 엔드포인트를 통해 토큰을 얻을 수 있습니다. ### 추론 **POST** `/api/v1/inference` **GET** `/api/v1/inference/{id}` ### 모델 **GET** `/api/v1/models` **POST** `/api/v1/models` **POST** `/api/v1/models/{name}/promote` ### OpenAPI 전체 OpenAPI 스펙은 실행 중인 모든 장치의 `/api/v1/openapi.json`에 게시됩니다.