📋 기획 및 설계: - 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>
12 KiB
12 KiB
팜큐(FARMQ) Proxmox VNC 통합 시스템 기획서
🎯 프로젝트 개요
목표
Flask Admin 웹 인터페이스에서 한 번의 클릭으로 Proxmox VM의 VNC 화면에 접속할 수 있는 통합 시스템 구축
핵심 아이디어
- Headscale 네트워크를 통해 모든 약국의 Proxmox 호스트에 접근 가능
- Proxmox VNC API를 활용하여 VM 화면 원격 제어
- 브라우저 기반 VNC 클라이언트 (Guacamole/noVNC)로 즉시 접속
- 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 목록 자동 동기화
- 권한 검증 시스템
- 에러 처리 및 로깅
- 성능 최적화
🚀 배포 계획
개발 환경 테스트
- 로컬 Proxmox 테스트: VirtualBox/VMware로 Proxmox VE 설치
- noVNC 연동 테스트: 기본 VNC 연결 확인
- Headscale 네트워크 테스트: 원격 Proxmox 접근
운영 환경 적용
- 점진적 배포: 1개 약국부터 테스트
- 모니터링 시스템: VNC 연결 로그 및 성능 측정
- 백업 접근 방법: VNC 실패 시 SSH/RDP 대안
💡 추가 기능 아이디어
Phase 2 고급 기능
- 다중 모니터 지원: VM이 여러 화면을 사용하는 경우
- 클립보드 공유: 로컬 PC ↔ 원격 VM 텍스트 복사
- 파일 전송: 드래그앤드롭 파일 업로드
- 스크린샷 캡처: 문제 해결을 위한 화면 저장
- 세션 녹화: 작업 과정 기록
모니터링 및 분석
- VNC 사용 통계: 접속 시간, 빈도 분석
- VM 성능 모니터링: CPU, 메모리 사용률 실시간 표시
- 접속 이력 관리: 언제, 누가, 어떤 VM에 접속했는지 로그
🎯 예상 효과
업무 효율성
- 즉시 원격 지원: 약국 직원 도움 요청 시 바로 화면 접속
- 중앙 집중 관리: 모든 약국 VM을 한 곳에서 관리
- 문제 해결 시간 단축: 전화 설명 → 직접 화면 제어
기술적 장점
- Headscale 네트워크 활용: 기존 인프라 최대한 활용
- 브라우저 기반: 별도 소프트웨어 설치 불필요
- 확장성: 새로운 약국 추가 시 자동 연동
📞 문의 및 지원
이 시스템 구현 과정에서 기술적 이슈나 추가 요구사항이 있으면 언제든 문의하세요.
핵심 목표: 🖱️ 원클릭 → 🖥️ VM 화면 접속