headscale-tailscale-replace.../PROXMOX_VNC_INTEGRATION_PLAN.md
시골약사 35ecd4748e PharmQ SaaS 구독 서비스 관리 시스템 완전 구현
📋 기획 및 설계:
- PharmQ SaaS 서비스 기획서 작성
- 구독 서비스 라인업 정의 (클라우드PC, AI CCTV, CRM)
- DB 스키마 설계 및 API 아키텍처 설계

🗄️ 데이터베이스 구조:
- service_products: 서비스 상품 마스터 테이블
- pharmacy_subscriptions: 약국별 구독 현황 테이블
- subscription_usage_logs: 서비스 이용 로그 테이블
- billing_history: 결제 이력 테이블
- 샘플 데이터 자동 생성 (21개 구독, 월 118만원 매출)

🔧 백엔드 API 구현:
- 구독 현황 통계 API (/api/subscriptions/stats)
- 약국별 구독 조회 API (/api/pharmacies/subscriptions)
- 구독 상세 정보 API (/api/pharmacy/{id}/subscriptions)
- 구독 생성/해지 API (/api/subscriptions)

🖥️ 프론트엔드 UI 구현:
- 대시보드 구독 현황 카드 (월 매출, 구독 수, 구독률 등)
- 약국 목록에 구독 상태 아이콘 및 월 구독료 표시
- 약국 상세 페이지 구독 서비스 섹션 추가
- 실시간 구독 생성/해지 기능 구현

 주요 특징:
- 서비스별 색상 코딩 및 이모지 아이콘 시스템
- 실시간 업데이트 (구독 생성/해지 즉시 반영)
- 반응형 디자인 (모바일/태블릿 최적화)
- 툴팁 기반 상세 정보 표시

📊 현재 구독 현황:
- 총 월 매출: ₩1,180,000
- 구독 약국: 10/14개 (71.4%)
- AI CCTV: 6개 약국, CRM: 10개 약국, 클라우드PC: 5개 약국

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-11 19:48:12 +09:00

12 KiB

팜큐(FARMQ) Proxmox VNC 통합 시스템 기획서

🎯 프로젝트 개요

목표

Flask Admin 웹 인터페이스에서 한 번의 클릭으로 Proxmox VM의 VNC 화면에 접속할 수 있는 통합 시스템 구축

핵심 아이디어

  1. Headscale 네트워크를 통해 모든 약국의 Proxmox 호스트에 접근 가능
  2. Proxmox VNC API를 활용하여 VM 화면 원격 제어
  3. 브라우저 기반 VNC 클라이언트 (Guacamole/noVNC)로 즉시 접속
  4. Flask Admin 버튼VNC 화면 원클릭 연결

🏗️ 시스템 아키텍처

[Flask Admin Dashboard]
        ↓ (클릭)
[VNC 연결 요청 API]
        ↓
[Headscale 네트워크]
        ↓ (100.64.0.x)
[Proxmox Host Server]
        ↓ (VNC API)
[VM VNC Console]
        ↓
[noVNC/Guacamole Web Client]
        ↓
[사용자 브라우저]

📋 기술 스택

Frontend

  • noVNC: HTML5 VNC 클라이언트 (가벼움, 쉬운 통합)
  • Apache Guacamole: 더 고급 기능 (클립보드, 파일 전송)
  • Bootstrap 5: UI 프레임워크

Backend

  • Flask: 웹 서버 및 API
  • Proxmox VE API: VM 관리 및 VNC 토큰 생성
  • WebSocket Proxy: VNC 트래픽 중계

Network

  • Headscale: 팜큐 네트워크 (100.64.0.0/10)
  • Tailscale: 각 Proxmox 호스트 연결

🔧 구현 단계

Phase 1: Proxmox API 통합 (1-2일)

1.1 Proxmox API 클라이언트 구현

# utils/proxmox_client.py
class ProxmoxClient:
    def __init__(self, host, username, password):
        self.host = host  # 100.64.0.x (Headscale IP)
        
    def get_vm_list(self):
        """VM 목록 조회"""
        
    def get_vnc_ticket(self, vmid):
        """VNC 접속 티켓 생성"""
        
    def get_vm_status(self, vmid):
        """VM 상태 확인"""

1.2 데이터베이스 모델 확장

# models/farmq_models.py
class ProxmoxVM(FarmqBase):
    __tablename__ = 'proxmox_vms'
    
    id = Column(Integer, primary_key=True)
    pharmacy_id = Column(Integer, ForeignKey('pharmacy_info.id'))
    proxmox_host_ip = Column(String(15))  # 100.64.0.x
    vmid = Column(Integer)
    vm_name = Column(String(100))
    vm_type = Column(String(50))  # windows, linux
    status = Column(String(20))   # running, stopped
    cpu_cores = Column(Integer)
    memory_mb = Column(Integer)
    created_at = Column(DateTime, default=datetime.now)
    updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now)

Phase 2: VNC 웹 클라이언트 구현 (2-3일)

2.1 noVNC 통합 (권장)

<!-- templates/vnc/novnc.html -->
<!DOCTYPE html>
<html>
<head>
    <title>{{ vm_name }} - VNC Console</title>
    <script src="/static/novnc/vnc.js"></script>
</head>
<body>
    <div id="vnc-container">
        <canvas id="vnc-canvas"></canvas>
    </div>
    
    <script>
        const rfb = new RFB(document.getElementById('vnc-canvas'), 
                           'ws://{{ flask_server }}/vnc/{{ session_id }}');
    </script>
</body>
</html>

2.2 WebSocket Proxy 서버

# vnc_proxy.py
import websockets
import asyncio

class VNCProxy:
    async def proxy_vnc_connection(self, websocket, path):
        # Flask → Proxmox VNC 연결 중계
        session_id = extract_session_id(path)
        proxmox_vnc = await connect_to_proxmox_vnc(session_id)
        
        # 양방향 데이터 중계
        await asyncio.gather(
            relay_websocket_to_vnc(websocket, proxmox_vnc),
            relay_vnc_to_websocket(proxmox_vnc, websocket)
        )

Phase 3: Flask Admin 통합 (1일)

3.1 VNC 연결 API 엔드포인트

# app.py
@app.route('/api/vm/<int:vm_id>/vnc', methods=['POST'])
def connect_vm_vnc(vm_id):
    """VM VNC 연결 세션 생성"""
    try:
        vm = get_vm_by_id(vm_id)
        proxmox = ProxmoxClient(vm.proxmox_host_ip, username, password)
        
        # VNC 티켓 생성
        vnc_ticket = proxmox.get_vnc_ticket(vm.vmid)
        
        # 세션 생성
        session_id = create_vnc_session(vm_id, vnc_ticket)
        
        return jsonify({
            'session_id': session_id,
            'vnc_url': f'/vnc/console/{session_id}',
            'vm_info': vm.to_dict()
        })
    except Exception as e:
        return jsonify({'error': str(e)}), 500

@app.route('/vnc/console/<session_id>')
def vnc_console(session_id):
    """VNC 콘솔 페이지"""
    session = get_vnc_session(session_id)
    return render_template('vnc/novnc.html', 
                         session=session,
                         vm_name=session['vm_name'])

3.2 약국 관리 페이지 업데이트

<!-- templates/pharmacy/detail.html -->
<div class="card">
    <div class="card-header">
        <h5>가상 머신 목록</h5>
    </div>
    <div class="card-body">
        <table class="table">
            <thead>
                <tr>
                    <th>VM 이름</th>
                    <th>상태</th>
                    <th>타입</th>
                    <th>리소스</th>
                    <th>액션</th>
                </tr>
            </thead>
            <tbody>
                {% for vm in pharmacy_vms %}
                <tr>
                    <td>{{ vm.vm_name }}</td>
                    <td>
                        {% if vm.status == 'running' %}
                            <span class="badge bg-success">실행 중</span>
                        {% else %}
                            <span class="badge bg-secondary">정지</span>
                        {% endif %}
                    </td>
                    <td>{{ vm.vm_type }}</td>
                    <td>{{ vm.cpu_cores }}C / {{ vm.memory_mb }}MB</td>
                    <td>
                        {% if vm.status == 'running' %}
                            <button class="btn btn-primary btn-sm" 
                                    onclick="openVNC({{ vm.id }})">
                                <i class="fas fa-desktop"></i> VNC 접속
                            </button>
                        {% endif %}
                        <button class="btn btn-info btn-sm" 
                                onclick="showVMDetails({{ vm.id }})">
                            <i class="fas fa-info-circle"></i> 상세
                        </button>
                    </td>
                </tr>
                {% endfor %}
            </tbody>
        </table>
    </div>
</div>

<script>
async function openVNC(vmId) {
    try {
        showSpinner('VNC 연결 준비 중...');
        
        const response = await fetch(`/api/vm/${vmId}/vnc`, {
            method: 'POST'
        });
        
        const data = await response.json();
        
        if (response.ok) {
            // 새 탭에서 VNC 콘솔 열기
            window.open(data.vnc_url, '_blank', 
                       'width=1024,height=768,scrollbars=yes,resizable=yes');
        } else {
            showToast(data.error, 'error');
        }
    } catch (error) {
        showToast('VNC 연결 실패: ' + error.message, 'error');
    } finally {
        hideSpinner();
    }
}
</script>

📊 데이터 흐름

1. VM 목록 동기화

Proxmox API → Flask Backend → Database → Admin Dashboard

2. VNC 연결 프로세스

1. 사용자가 "VNC 접속" 버튼 클릭
2. Flask API가 Proxmox API 호출하여 VNC 티켓 생성
3. WebSocket 프록시 세션 생성
4. 새 브라우저 탭에서 noVNC 클라이언트 실행
5. 실시간 VM 화면 표시

🔐 보안 고려사항

인증 및 권한

# 약국별 VM 접근 권한 검증
def check_vm_access_permission(user_id, vm_id):
    """사용자가 해당 VM에 접근 권한이 있는지 확인"""
    user_pharmacy = get_user_pharmacy(user_id)
    vm_pharmacy = get_vm_pharmacy(vm_id)
    return user_pharmacy.id == vm_pharmacy.id

# VNC 세션 시간 제한
VNC_SESSION_TIMEOUT = 3600  # 1시간

네트워크 보안

  • Headscale 네트워크 내부에서만 Proxmox 접근
  • HTTPS/WSS 암호화 통신
  • 세션 기반 일회성 VNC 토큰

🎨 UI/UX 설계

메인 대시보드

┌─────────────────────────────────────────┐
│ 📊 팜큐 관리 대시보드                      │
├─────────────────────────────────────────┤
│ 약국: 세종온누리약국                       │
│ ┌─────────┬──────────┬────────┬─────────┐ │
│ │ VM 이름  │ 상태      │ 타입    │ 액션    │ │
│ ├─────────┼──────────┼────────┼─────────┤ │
│ │ POS-01  │ 🟢 실행중 │ Win11  │ [VNC접속]│ │
│ │ SERVER  │ 🟢 실행중 │ Ubuntu │ [VNC접속]│ │
│ │ BACKUP  │ ⚪ 정지   │ Win10  │ [시작]  │ │
│ └─────────┴──────────┴────────┴─────────┘ │
└─────────────────────────────────────────┘

VNC 콘솔 화면

┌─────────────────────────────────────────┐
│ 🖥️ POS-01 (Windows 11) - VNC Console   │
├─────────────────────────────────────────┤
│ [전체화면] [클립보드] [Ctrl+Alt+Del]      │
├─────────────────────────────────────────┤
│                                         │
│         VM 화면이 여기에 표시            │
│            (noVNC Canvas)               │
│                                         │
└─────────────────────────────────────────┘

📋 구현 체크리스트

Backend (Flask)

  • Proxmox API 클라이언트 구현
  • ProxmoxVM 데이터 모델 생성
  • VNC 세션 관리 시스템
  • WebSocket 프록시 서버
  • API 엔드포인트 구현

Frontend (Templates)

  • noVNC 라이브러리 통합
  • VNC 콘솔 페이지 템플릿
  • 약국 상세 페이지 VM 섹션
  • JavaScript VNC 연결 함수

시스템 통합

  • VM 목록 자동 동기화
  • 권한 검증 시스템
  • 에러 처리 및 로깅
  • 성능 최적화

🚀 배포 계획

개발 환경 테스트

  1. 로컬 Proxmox 테스트: VirtualBox/VMware로 Proxmox VE 설치
  2. noVNC 연동 테스트: 기본 VNC 연결 확인
  3. Headscale 네트워크 테스트: 원격 Proxmox 접근

운영 환경 적용

  1. 점진적 배포: 1개 약국부터 테스트
  2. 모니터링 시스템: VNC 연결 로그 및 성능 측정
  3. 백업 접근 방법: VNC 실패 시 SSH/RDP 대안

💡 추가 기능 아이디어

Phase 2 고급 기능

  • 다중 모니터 지원: VM이 여러 화면을 사용하는 경우
  • 클립보드 공유: 로컬 PC ↔ 원격 VM 텍스트 복사
  • 파일 전송: 드래그앤드롭 파일 업로드
  • 스크린샷 캡처: 문제 해결을 위한 화면 저장
  • 세션 녹화: 작업 과정 기록

모니터링 및 분석

  • VNC 사용 통계: 접속 시간, 빈도 분석
  • VM 성능 모니터링: CPU, 메모리 사용률 실시간 표시
  • 접속 이력 관리: 언제, 누가, 어떤 VM에 접속했는지 로그

🎯 예상 효과

업무 효율성

  • 즉시 원격 지원: 약국 직원 도움 요청 시 바로 화면 접속
  • 중앙 집중 관리: 모든 약국 VM을 한 곳에서 관리
  • 문제 해결 시간 단축: 전화 설명 → 직접 화면 제어

기술적 장점

  • Headscale 네트워크 활용: 기존 인프라 최대한 활용
  • 브라우저 기반: 별도 소프트웨어 설치 불필요
  • 확장성: 새로운 약국 추가 시 자동 연동

📞 문의 및 지원

이 시스템 구현 과정에서 기술적 이슈나 추가 요구사항이 있으면 언제든 문의하세요.

핵심 목표: 🖱️ 원클릭🖥️ VM 화면 접속