Files
whisper-stt/README.md

16 KiB

VoiceScript — 음성 변환(STT) + 이미지 인식(OCR) 통합 툴

Debian OS + Docker Compose 기반 자체 호스팅 서비스
faster-whisper(STT) + PaddleOCR 3.x / Ollama Vision(OCR) 듀얼 백엔드


목차

  1. 기능 개요
  2. 프로젝트 구조
  3. 시스템 요구사항
  4. 설치 전 필수 확인사항 ⚠️
  5. 환경 변수 설정
  6. 빌드 및 실행
  7. Nginx 연동 SSL
  8. Ollama 모델 준비
  9. 운영 관리
  10. 트러블슈팅 알려진 이슈
  11. API 엔드포인트

기능 개요

🎙 STT — 음성 텍스트 변환

  • 엔진: faster-whisper (OpenAI Whisper 최적화 포크)
  • 지원 형식: mp3 wav m4a ogg flac aac mp4 webm mkv
  • VAD(무음 구간 자동 제거) 적용
  • 타임스탬프 세그먼트 분리 출력
  • TXT 파일 다운로드

🔍 OCR — 이미지 텍스트 인식

  • 지원 형식: jpg png bmp tiff webp gif
  • PaddleOCR 모드: 로컬 실행, 표 구조 분석(PP-Structure), Excel 다운로드
  • Ollama Vision 모드: 기존 Ollama 서버 활용, 자연어 지시, 커스텀 프롬프트

🔐 인증

  • JWT 기반 로그인 (만료 시간 설정 가능)
  • 모든 API 토큰 인증 필수

프로젝트 구조

whisper-stt/
│
├── docker-compose.yml          # 전체 서비스 정의
│
├── app/
│   ├── Dockerfile              # Python 3.11-slim + ffmpeg + PaddlePaddle 3.0.0
│   ├── requirements.txt        # Python 패키지 목록
│   │
│   ├── main.py                 # FastAPI 앱 (인증 + STT + OCR 엔드포인트)
│   ├── auth.py                 # JWT 인증 모듈
│   ├── tasks.py                # Celery STT 태스크 (faster-whisper)
│   ├── ocr_tasks.py            # Celery OCR 태스크 (PaddleOCR / Ollama)
│   │
│   └── static/
│       └── index.html          # 웹 프론트엔드 (로그인 + STT + OCR 탭)
│
└── nginx/                      # 참고용 (호스트 Nginx 사용 시 불필요)
    ├── Dockerfile
    └── nginx.conf

컨테이너 구성

┌─────────────────────────────────────────┐
│  호스트 Nginx (SSL/certbot)              │
│  → 리버스 프록시 → 127.0.0.1:8800       │
└─────────────────────────────────────────┘
           │
           ▼
┌──────────────────┐    ┌──────────────────────┐
│   whisper_app    │    │   whisper_worker     │
│   FastAPI:8000   │    │   Celery (solo pool) │
│   (포트 8800)    │    │   STT + OCR 처리     │
└────────┬─────────┘    └──────────┬───────────┘
         │                         │
         └────────────┬────────────┘
                      ▼
           ┌──────────────────┐
           │  whisper_redis   │
           │  Redis:6379      │
           │  (작업 큐/결과)  │
           └──────────────────┘

시스템 요구사항

항목 최소 권장
CPU 4코어 AMD 5825u 이상
RAM 8GB 16GB (medium 모델 기준)
디스크 20GB 50GB 이상
OS Debian 11+ Debian 12 (Bookworm)
Docker 24.0+ 최신
Docker Compose v2.0+ 최신 (version: 필드 불필요)

의존 서비스

  • Ollama: 호스트에서 11434 포트로 실행 중이어야 함 (OCR Vision 모드 사용 시)

설치 전 필수 확인사항

⚠️ 이 섹션을 건너뛰면 빌드 후 오류가 발생합니다.

1. 호스트 IP 확인 — OLLAMA_URL 설정

host.docker.internal은 Linux에서 동작하지 않습니다.
반드시 실제 LAN IP를 확인하여 설정하세요.

ip addr show | grep "inet " | grep -v 127.0.0.1

docker-compose.yml 두 곳(app, worker) 모두 변경:

- OLLAMA_URL=http://실제호스트IP:11434

2. 인증 정보 변경

# app, worker 두 서비스 모두 동일하게 변경
- AUTH_USERNAME=원하는아이디
- AUTH_PASSWORD=강력한비밀번호
- JWT_SECRET=랜덤문자열   # openssl rand -hex 32
# JWT 시크릿 생성
openssl rand -hex 32

3. 포트 충돌 확인

ss -tlnp | grep 8800

충돌 시 docker-compose.yml에서 변경:

ports:
  - "원하는포트:8000"

4. 디스크 용량 확인

항목 크기 시점
Whisper medium 모델 ~1.5GB 첫 STT 실행 시 자동 다운로드
PaddleOCR korean 모델 ~700MB 첫 OCR 실행 시 자동 다운로드
PaddlePaddle 3.0.0 ~300MB 빌드 시
Docker 이미지 ~3GB 빌드 시
df -h /
# 여유 공간 10GB 이상 권장

5. Ollama 서버 실행 확인

curl http://localhost:11434/api/tags
# 응답 없으면 Ollama 미실행 상태

6. Docker Compose v2 확인

docker compose version
# v2.x 이상이어야 함 (docker-compose가 아닌 docker compose)

환경 변수 설정

docker-compose.ymlappworker 두 서비스에 동일하게 설정.

인증

변수 기본값 설명
AUTH_USERNAME admin 로그인 아이디
AUTH_PASSWORD changeme1234 로그인 비밀번호 변경 필수
JWT_SECRET (변경 필수) JWT 서명 키
JWT_EXPIRE_HOURS 12 토큰 유효 시간 (시간 단위)

Whisper STT

변수 기본값 설명
WHISPER_MODEL medium tiny base small medium large-v3
WHISPER_DEVICE cpu GPU 없는 경우 cpu
WHISPER_COMPUTE_TYPE int8 CPU 최적화: int8 권장
WHISPER_LANGUAGE ko 언어 고정. 비우면 자동 감지
WHISPER_BEAM_SIZE 5 정확도↑ vs 속도↓
WHISPER_INITIAL_PROMPT 비어있음 도메인 힌트 예: "고객 상담 녹취록입니다."

모델별 성능 (5825u CPU 기준)

모델 크기 1분 변환 시간 한국어 정확도
tiny 75MB ~5초 보통
base 145MB ~10초 보통
small 484MB ~30초 양호
medium 1.5GB ~90초 우수 ← 권장
large-v3 3GB ~5분+ 최고

PaddleOCR

변수 기본값 설명
OCR_LANG korean korean en japan chinese_cht ch

Ollama OCR

변수 기본값 설명
OLLAMA_URL http://192.168.0.126:11434 실제 호스트 IP로 변경 필수
OLLAMA_TIMEOUT 180 초 단위. 11b 이상 모델은 300 이상 권장

파일 관리

변수 기본값 설명
MAX_UPLOAD_MB 500 업로드 최대 파일 크기 (MB)
OUTPUT_KEEP_HOURS 48 결과 파일 보관 시간. 0=무제한

빌드 및 실행

# 1. 저장소 클론
git clone http://gitea.byunc.com/byun/whisper-stt.git
cd whisper-stt

# 2. 필수 설정 변경 (docker-compose.yml)
#    - AUTH_USERNAME, AUTH_PASSWORD, JWT_SECRET
#    - OLLAMA_URL (호스트 실제 IP)

# 3. 빌드 및 시작
docker compose up -d --build

# 4. 빌드 후 모델 다운로드 완료까지 대기
docker compose logs -f worker
# "[Whisper] 로드 완료" + "celery@... ready." 확인

접속:

http://서버IP:8800

이후 코드 변경 시 재배포

# 코드만 변경된 경우 (재빌드 필요)
docker compose build --no-cache app worker
docker compose up -d

# 환경변수만 변경된 경우 (재빌드 불필요)
docker compose up -d --force-recreate app worker

# Docker 이미지 정리 (빌드 반복 후 용량 정리)
docker system prune -f

Nginx 연동 SSL

호스트 Nginx + certbot SSL 운용 중인 경우:

# /etc/nginx/sites-available/voicescript.conf

server {
    listen 443 ssl;
    server_name stt.yourdomain.com;

    ssl_certificate     /etc/letsencrypt/live/stt.yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/stt.yourdomain.com/privkey.pem;

    # ⚠️ 음성 파일 업로드를 위해 반드시 설정 (기본 1MB → 초과 시 413 에러)
    client_max_body_size 500M;
    client_body_timeout  300s;
    proxy_read_timeout   600s;
    proxy_send_timeout   600s;

    location / {
        proxy_pass         http://127.0.0.1:8800;
        proxy_set_header   Host              $host;
        proxy_set_header   X-Real-IP         $remote_addr;
        proxy_set_header   X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header   X-Forwarded-Proto $scheme;
    }
}

server {
    listen 80;
    server_name stt.yourdomain.com;
    return 301 https://$host$request_uri;
}
sudo certbot --nginx -d stt.yourdomain.com
sudo nginx -t && sudo systemctl reload nginx

Ollama 모델 준비

호스트에서 미리 pull:

# 문서/표 특화 — 약 2GB ← 기본값, 권장
ollama pull granite3.2-vision

# OCR 전용 경량 — 약 2GB
ollama pull deepseek-ocr:3b

# 범용 고정확도 — 약 8GB (RAM 16GB+ 필요)
ollama pull llama3.2-vision:11b

# 최고 정확도 — 약 9GB (RAM 16GB+ 필요)
ollama pull richardyoung/olmocr2:7b-q8

참고: granite3.2-vision만 설치되어 있어도 즉시 사용 가능합니다.
큰 모델 사용 시 OLLAMA_TIMEOUT=300 이상으로 설정하세요.


운영 관리

# 상태 확인
docker compose ps

# 로그 확인
docker compose logs app --tail=30
docker compose logs worker --tail=30
docker compose logs -f              # 전체 실시간

# 재시작
docker compose restart

# 중지
docker compose down

# 설정 변경 후 재시작 (재빌드 없이)
docker compose up -d --force-recreate app worker

Docker 이미지 정리

빌드를 반복하면 dangling 이미지가 누적됩니다.

docker system df                    # 사용량 확인
docker system prune -f              # 불필요한 이미지/컨테이너 정리
docker compose down -v              # 볼륨 포함 완전 초기화 (모델 재다운로드 필요)

볼륨 정보

볼륨 내용 삭제 시 영향
whisper_models Whisper 모델 (~1.5GB) 재다운로드 필요
paddle_models PaddleOCR 모델 (~700MB) 재다운로드 필요
stt_data 업로드/결과 파일 데이터 손실
redis_data 작업 큐 상태 진행 중 작업 손실

트러블슈팅 알려진 이슈

실제 배포 과정에서 겪은 오류와 해결 방법입니다.


signal 11 (SIGSEGV) — Worker 크래시

원인: faster-whisper 내부 CTranslate2 라이브러리가 Celery prefork 방식과 충돌
해결: docker-compose.yml worker command에 --pool=solo 추가

command: >
  celery -A tasks worker
  --loglevel=info
  --pool=solo          # ← 이 옵션이 핵심
  --max-tasks-per-child=50
  -Q stt,ocr

--pool=solo는 포크 없이 메인 프로세스에서 직접 실행합니다.
--concurrency=1이었으므로 성능 차이는 없습니다.


No matching distribution found for paddlepaddle==2.6.1

원인: 미러에서 해당 버전 제거됨
해결: Dockerfile에서 3.0.0으로 변경

RUN pip install --no-cache-dir paddlepaddle==3.0.0 \
    -i https://pypi.tuna.tsinghua.edu.cn/simple

ValueError: password cannot be longer than 72 bytes

원인: passlib[bcrypt] 초기화 버그
해결: auth.py에서 bcrypt 완전 제거, 직접 문자열 비교 방식 사용
requirements.txt에서 passlib 줄 삭제


AttributeError: 'DisabledBackend'

원인: from celery.result import AsyncResult 사용 시 백엔드 설정 누락
해결: celery_app.AsyncResult() 방식으로 변경

# main.py
from tasks import celery_app
r = celery_app.AsyncResult(task_id)  # ✅

ModuleNotFoundError: No module named 'ocr_tasks'

원인: celery_app.autodiscover_tasks(["ocr_tasks"]) 동작 안 함
해결: tasks.py에서 직접 import

from ocr_tasks import ocr_task  # noqa: F401

Unknown argument: use_gpu / Unknown argument: show_log

원인: PaddleOCR 3.x에서 파라미터 제거됨
해결: ocr_tasks.py에서 해당 파라미터 삭제

_ocr_engine = PaddleOCR(use_angle_cls=True, lang=OCR_LANG)  # ✅

PaddleOCR.predict() got an unexpected keyword argument 'cls'

원인: PaddleOCR 3.x API 변경
해결: ocr(img, cls=True)ocr(img)


'AnalysisConfig' object has no attribute 'set_optimization_level'

원인: PaddleOCR 3.x와 paddlepaddle 2.x 버전 불일치
해결: paddlepaddle 3.0.0으로 업그레이드


too many values to unpack (expected 2)

원인: PaddleOCR 3.x 결과 구조 변경
해결: rec_texts / rec_scores 방식으로 파싱

r = result[0]
texts  = r.get("rec_texts", [])
scores = r.get("rec_scores", [])

MISCONF Redis is configured to save RDB snapshots

원인: 디스크 부족으로 Redis RDB 저장 실패 → 쓰기 차단
해결: docker-compose.yml Redis command에 옵션 추가

command: redis-server --stop-writes-on-bgsave-error no

Ollama 연결 타임아웃

원인: host.docker.internal이 Linux에서 불안정
해결: 실제 호스트 LAN IP로 변경

- OLLAMA_URL=http://192.168.x.x:11434

STT 진행률 5%/15%에서 멈춤

단계 원인 대기 시간
5% 모델 준비 중 Whisper 모델 첫 다운로드 (~1.5GB) 5~20분
15% 오디오 분석 중 첫 변환 시 내부 초기화 1~3분
변환 중... Xs / Xs 정상 진행 파일 길이에 비례
# 진행 상황 실시간 확인
docker compose logs worker -f

API 엔드포인트

인증

메서드 경로 설명
POST /api/login 로그인 (username, password form)
GET /api/me 현재 사용자 확인

STT

메서드 경로 설명
POST /api/transcribe 음성 파일 업로드 및 변환 시작
GET /api/status/{task_id} 작업 진행 상태 조회
GET /api/download/{filename} 결과 파일 다운로드

OCR

메서드 경로 설명
POST /api/ocr 이미지 업로드 및 인식 시작

OCR 파라미터

파라미터 기본값 설명
file 이미지 파일
mode text text | structure
backend paddle paddle | ollama
ollama_model granite3.2-vision Ollama 모델명
custom_prompt 비어있음 Ollama 커스텀 프롬프트

관리

메서드 경로 설명
POST /api/cleanup 오래된 결과 파일 정리

기술 스택

구성요소 버전 역할
Python 3.11 런타임
FastAPI 0.115 API 서버
Celery 5.4 (--pool=solo) 비동기 태스크 큐
Redis 7 alpine 메시지 브로커
faster-whisper 1.0.3 STT 엔진
PaddlePaddle 3.0.0 OCR 딥러닝 프레임워크
PaddleOCR 3.x OCR 엔진
httpx 0.27+ Ollama API 호출
Ollama 호스트 운용 Vision 모델 서버