Dify 기반 AI 애플리케이션 온프레미스 마이그레이션 도구 설계

아키텍처 개요

클라우드 기반 AI 개발 플랫폼에서 Dify 온프레미스 환경으로의 원활한 전환을 목표로 하는 마이그레이션 파이프라인을 설계한다. 이 도구는 프로젝트 메타데이터 추출, 환경별 설정 변환, 컨테이너 이지 빌드까지 전 과정을 자동화한다.

핵심 모듈 구성

1. 프로젝트 파싱 엔진

원본 플랫폼의 프로젝트 아카이브를 분석하여 다음 요소를 식별한다:

  • LLM 프로바이더 설정 및 API 엔드포인트
  • 프롬프트 템플릿과 변수 스키마
  • 외부 도구(툴) 호출 정의
  • 지식베이스 임베딩 모델 및 벡터 저장소 연결
# parser/structure_analyzer.py
import json
from dataclasses import dataclass
from pathlib import Path
from typing import Optional

@dataclass
class ModelConfig:
    provider: str
    endpoint: str
    credential_key: str
    temperature: float = 0.7
    max_tokens: int = 2048

class ProjectAnalyzer:
    def __init__(self, archive_path: str):
        self.root = Path(archive_path)
        self.manifest: Optional[dict] = None
    
    def extract_workflow(self) -> dict:
        workflow_file = self.root / "workflow.json"
        with open(workflow_file, encoding="utf-8") as f:
            data = json.load(f)
        return self._normalize_nodes(data)
    
    def _normalize_nodes(self, raw: dict) -> dict:
        # 노드 타입을 Dify 호환 형태로 변환
        node_map = {
            "llm_call": "llm",
            "knowledge_retrieve": "knowledge-retrieval",
            "code_execute": "code"
        }
        nodes = raw.get("nodes", [])
        for node in nodes:
            node["type"] = node_map.get(node["type"], node["type"])
        return {"nodes": nodes, "edges": raw.get("edges", [])}

2. Dify 설정 생성기

추출된 메타데이터를 Dify의 docker-compose.yml 및 환경변수 체계에 맞춰 재구성한다.

# generator/dify_builder.py
import os
from jinja2 import Environment, FileSystemLoader

class DifyConfigBuilder:
    def __init__(self, template_dir: str = "templates"):
        self.env = Environment(loader=FileSystemLoader(template_dir))
    
    def generate_compose(self, services: list) -> str:
        tpl = self.env.get_template("docker-compose.yml.j2")
        return tpl.render(
            api_image="langgenius/dify-api:0.6.9",
            web_image="langgenius/dify-web:0.6.9",
            sandbox_image="langgenius/dify-sandbox:0.2.1",
            enabled_services=services
        )
    
    def build_env_file(self, model_cfg: ModelConfig, db_url: str) -> str:
        lines = [
            f"OPENAI_API_KEY={model_cfg.credential_key}",
            f"OPENAI_API_BASE={model_cfg.endpoint}",
            f"DB_DATABASE_URL={db_url}",
            "REDIS_HOST=redis",
            "REDIS_PORT=6379",
            "SANDGBox_WORKERS=4",
            # 보안 강화: 기본 시크릿 무작위화
            f"SECRET_KEY={os.urandom(32).hex()}",
            "FORCE_HTTPS=true"
        ]
        return "\n".join(lines)

3. 컨테이너화 파이프라인

마이그레이션 결과물을 검증된 단일 이미지로 빌드하는 과정을 자동화한다.

# packager/image_assembler.py
import subprocess
import tempfile
from pathlib import Path

class ImageAssembler:
    def __init__(self, output_tag: str):
        self.tag = output_tag
        self.build_ctx = tempfile.TemporaryDirectory()
    
    def stage_assets(self, config_dir: Path, app_bundle: Path):
        """빌드 컨텍스트에 필요 파일 배치"""
        dest = Path(self.build_ctx.name)
        self._copytree(config_dir, dest / "config")
        self._copytree(app_bundle, dest / "app")
        # 멀티스테이지 Dockerfile 생성
        (dest / "Dockerfile").write_text(self._render_dockerfile())
    
    def _render_dockerfile(self) -> str:
        return '''
FROM langgenius/dify-api:0.6.9 as base
WORKDIR /app/api
COPY config /app/api/configs
COPY app /app/api/custom_extensions
ENV MODE=production
RUN pip install --no-cache-dir -r custom_extensions/requirements.txt
EXPOSE 5001
CMD ["gunicorn", "-w", "4", "-b", "0.0.0.0:5001", "app:app"]
'''
    
    def build(self) -> str:
        cmd = ["docker", "build", "-t", self.tag, self.build_ctx.name]
        result = subprocess.run(cmd, capture_output=True, text=True)
        if result.returncode != 0:
            raise RuntimeError(f"빌드 실패: {result.stderr}")
        return f"{self.tag} 이미지 빌드 완료"

React 기반 설정 조정 인터페이스

마이그레이션 전 파라미터를 시각적으로 검토하고 수정할 수 있는 대시보드를 구현한다.

// src/components/ConfigTuner.tsx
import { useState } from 'react';

interface TuningParams {
  temperature: number;
  topP: number;
  presencePenalty: number;
  maxContextLength: number;
}

export default function ConfigTuner({ 
  initialConfig, 
  onValidate 
}: {
  initialConfig: TuningParams;
  onValidate: (cfg: TuningParams) => boolean;
}) {
  const [params, setParams] = useState<TuningParams>(initialConfig);
  const [errors, setErrors] = useState>({});

  const handleSliderChange = (field: keyof TuningParams, value: number) => {
    const updated = { ...params, [field]: value };
    setParams(updated);
    
    // 실시간 유효성 검사
    const isValid = onValidate(updated);
    if (!isValid) {
      setErrors(prev => ({ ...prev, [field]: '권장 범위 초과' }));
    } else {
      setErrors(prev => { const n = { ...prev }; delete n[field]; return n; });
    }
  };

  return (
    <div className="tuner-panel">
      <div className="control-group">
        <label>Temperature: {params.temperature}</label>
        <input
          type="range"
          min="0"
          max="2"
          step="0.05"
          value={params.temperature}
          onChange={e => handleSliderChange('temperature', parseFloat(e.target.value))}
        />
        {errors.temperature && {errors.temperature}}
      </div>
      
      <div className="control-group">
        <label>Max Context: {params.maxContextLength}</label>
        <input
          type="range"
          min="1024"
          max="128000"
          step="1024"
          value={params.maxContextLength}
          onChange={e => handleSliderChange('maxContextLength', parseInt(e.target.value))}
        />
      </div>
    </div>
  );
}

보안 설계 원칙

계층적용 방안
전송TLS 1.3 강제, mTLS 상호 인증
저장PostgreSQL TDE, Redis AUTH + TLS
비밀값HashiCorp Vault 연동, 런타임 주입
이미지Distroless 베이스, 비루트 실행, CVE 스캔
네트워크내부 서비스 간 정책 기반 격리

배포 검증 체크리스트

  1. 컨테이너 헬스체크 엔드포인트(/health) 응답 확인
  2. LLM 프로바이더 연결 테스트 및 토큰 계산 검증
  3. 지식베이스 문서 임베딩 및 유사도 검색 정확도 측정
  4. 동시 사용자 100명 부하 테스트
  5. 장애 복구 시나리오: DB 장애 시 서킷 브레이커 동작 확인

운영 자동화

# .github/workflows/deploy.yml
name: On-Premise Deploy
on:
  push:
    branches: [main]
jobs:
  build-and-deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: 마이그레이션 실행
        run: |
          python -m migrate_tool \
            --source "${{ secrets.CLOUD_PROJECT_URL }}" \
            --target-format dify \
            --output ./dify-package
      
      - name: 이미지 빌드 및 서명
        run: |
          cosign generate-key-pair
          docker build -t dify-local:${{ github.sha }} ./dify-package
          cosign sign --key cosign.key dify-local:${{ github.sha }}
      
      - name: 에어갭 환경으로 전달
        run: |
          docker save dify-local:${{ github.sha }} | \
            gpg --symmetric --cipher-algo AES256 > release.tar.gz.gpg

태그: Dify 온프레미스 AI 마이그레이션 docker React

6월 1일 17:41에 게시됨