상태 저장 기능을 개선하고, 상태 저장 시도 및 성공/실패 로그를 추가하였습니다. 또한, 세션 풀 상태 조회 방식을 최적화하고, 모니터링 대시보드의 데이터 수집 로직을 강화하였습니다. JSON 파일 포맷을 개선하여 가독성을 높였습니다.
This commit is contained in:
parent
4e1898463a
commit
5ee280ea27
|
|
@ -225,17 +225,16 @@ class SessionPool:
|
|||
"""세션 풀의 현재 상태를 반환합니다."""
|
||||
status_by_model = {}
|
||||
|
||||
all_sessions = list(self.pools.values()) # Flatten all sessions from all models
|
||||
|
||||
for model_type in ModelType:
|
||||
model_sessions = [s for s in all_sessions if s.model_type == model_type]
|
||||
in_use_count = sum(1 for s in model_sessions if s.in_use)
|
||||
available_count = len(model_sessions) - in_use_count
|
||||
pool = self.pools[model_type]
|
||||
total = len(pool)
|
||||
in_use = sum(1 for session in pool if session.in_use)
|
||||
available = total - in_use
|
||||
|
||||
status_by_model[model_type.value] = {
|
||||
"total": len(model_sessions),
|
||||
"in_use": in_use_count,
|
||||
"available": available_count
|
||||
"total": total,
|
||||
"in_use": in_use,
|
||||
"available": available
|
||||
}
|
||||
|
||||
return status_by_model
|
||||
|
|
|
|||
|
|
@ -310,7 +310,7 @@ class WorkerManager:
|
|||
"error": []
|
||||
}
|
||||
|
||||
for worker in self.workers:
|
||||
for worker_id, worker in self.workers.items():
|
||||
status_data = {
|
||||
"id": worker.worker_id,
|
||||
"status": worker.status.value,
|
||||
|
|
|
|||
|
|
@ -89,64 +89,131 @@ class MonitoringData:
|
|||
|
||||
async def collect_data(self) -> Dict[str, Any]:
|
||||
"""주기적으로 서버 상태 데이터를 수집합니다."""
|
||||
status = read_status_from_file()
|
||||
worker_status = status.get("worker_status", {})
|
||||
session_status = status.get("session_status", {})
|
||||
timestamp = status.get("timestamp", 0)
|
||||
|
||||
# 워커 매니저 상태 (안전하게 가져오기)
|
||||
try:
|
||||
worker_status = worker_manager.get_status() if worker_manager else self._get_default_worker_status()
|
||||
except Exception as e:
|
||||
logger.warning(f"워커 매니저 상태 조회 실패: {e}")
|
||||
worker_status = self._get_default_worker_status()
|
||||
|
||||
# 세션 풀 상태 (안전하게 가져오기)
|
||||
try:
|
||||
session_status = session_pool.get_status() if session_pool else self._get_default_session_status()
|
||||
except Exception as e:
|
||||
logger.warning(f"세션 풀 상태 조회 실패: {e}")
|
||||
session_status = self._get_default_session_status()
|
||||
|
||||
# Jetson 전용 정보 (안전하게 가져오기)
|
||||
jetson_info = {}
|
||||
if settings.IS_JETSON:
|
||||
try:
|
||||
jetson_info = gpu_monitor.get_jetson_specific_info()
|
||||
if jetson_info is None:
|
||||
jetson_info = {}
|
||||
except Exception as e:
|
||||
logger.warning(f"Jetson 전용 정보 조회 실패: {e}")
|
||||
jetson_info = {}
|
||||
|
||||
# API 통계
|
||||
api_stats = self._get_api_statistics()
|
||||
|
||||
# 알림 및 경고
|
||||
alerts = self._check_alerts(worker_status)
|
||||
|
||||
data = {
|
||||
"timestamp": datetime.now().isoformat(),
|
||||
"system_type": "Jetson Xavier" if settings.IS_JETSON else "x86_64",
|
||||
"gpu": {
|
||||
**gpu_monitor.get_gpu_memory_info(),
|
||||
"utilization": gpu_monitor.get_gpu_utilization()
|
||||
},
|
||||
"system_memory": gpu_monitor.get_system_memory_info(),
|
||||
"system_performance": self._get_system_performance(),
|
||||
"workers": worker_status,
|
||||
"sessions": session_status,
|
||||
"jetson": jetson_info,
|
||||
"api_stats": api_stats,
|
||||
"alerts": alerts
|
||||
}
|
||||
|
||||
# 히스토리에 추가
|
||||
self.history.append(data)
|
||||
if len(self.history) > self.max_history:
|
||||
self.history.pop(0)
|
||||
logger.info("데이터 수집 시작")
|
||||
|
||||
return data
|
||||
# status.json 파일에서 상태 읽기
|
||||
status = read_status_from_file()
|
||||
logger.info(f"status.json 읽기 완료: {bool(status)}")
|
||||
|
||||
worker_status = status.get("worker_status", {})
|
||||
session_status = status.get("session_status", {})
|
||||
api_stats = status.get("api_stats", {})
|
||||
timestamp = status.get("timestamp", 0)
|
||||
|
||||
logger.info(f"워커 상태: {bool(worker_status)}, 세션 상태: {bool(session_status)}")
|
||||
|
||||
# status.json에서 읽어온 데이터가 없으면 기본값 사용
|
||||
if not worker_status:
|
||||
logger.info("워커 상태가 비어있어 기본값 사용")
|
||||
worker_status = self._get_default_worker_status()
|
||||
if not session_status:
|
||||
logger.info("세션 상태가 비어있어 기본값 사용")
|
||||
session_status = self._get_default_session_status()
|
||||
|
||||
# GPU 정보 (안전하게 가져오기)
|
||||
gpu_info = {}
|
||||
try:
|
||||
logger.info("GPU 정보 수집 시작")
|
||||
gpu_info = {
|
||||
**gpu_monitor.get_gpu_memory_info(),
|
||||
"utilization": gpu_monitor.get_gpu_utilization()
|
||||
}
|
||||
logger.info("GPU 정보 수집 완료")
|
||||
except Exception as e:
|
||||
logger.warning(f"GPU 정보 조회 실패: {e}")
|
||||
gpu_info = {"total": 0, "used": 0, "free": 0, "usage_percent": 0, "utilization": 0}
|
||||
|
||||
# 시스템 메모리 정보 (안전하게 가져오기)
|
||||
system_memory = {}
|
||||
try:
|
||||
logger.info("시스템 메모리 정보 수집 시작")
|
||||
system_memory = gpu_monitor.get_system_memory_info()
|
||||
logger.info("시스템 메모리 정보 수집 완료")
|
||||
except Exception as e:
|
||||
logger.warning(f"시스템 메모리 정보 조회 실패: {e}")
|
||||
system_memory = {"total": 0, "used": 0, "free": 0, "usage_percent": 0}
|
||||
|
||||
# 시스템 성능 정보 (안전하게 가져오기)
|
||||
system_performance = {}
|
||||
try:
|
||||
logger.info("시스템 성능 정보 수집 시작")
|
||||
system_performance = self._get_system_performance()
|
||||
logger.info("시스템 성능 정보 수집 완료")
|
||||
except Exception as e:
|
||||
logger.warning(f"시스템 성능 정보 조회 실패: {e}")
|
||||
system_performance = {"cpu_percent": 0, "cpu_count": 1, "cpu_freq": 0}
|
||||
|
||||
# Jetson 전용 정보 (안전하게 가져오기)
|
||||
jetson_info = {}
|
||||
if settings.IS_JETSON:
|
||||
try:
|
||||
logger.info("Jetson 전용 정보 수집 시작")
|
||||
jetson_info = gpu_monitor.get_jetson_specific_info()
|
||||
if jetson_info is None:
|
||||
jetson_info = {}
|
||||
logger.info("Jetson 전용 정보 수집 완료")
|
||||
except Exception as e:
|
||||
logger.warning(f"Jetson 전용 정보 조회 실패: {e}")
|
||||
jetson_info = {}
|
||||
|
||||
# API 통계는 status.json에서 읽어온 것을 사용
|
||||
if not api_stats:
|
||||
logger.info("API 통계가 비어있어 기본값 사용")
|
||||
api_stats = self._get_api_statistics()
|
||||
|
||||
# 알림 및 경고 (안전하게 가져오기)
|
||||
alerts = []
|
||||
try:
|
||||
logger.info("알림 확인 시작")
|
||||
alerts = self._check_alerts(worker_status)
|
||||
logger.info("알림 확인 완료")
|
||||
except Exception as e:
|
||||
logger.warning(f"알림 확인 실패: {e}")
|
||||
alerts = []
|
||||
|
||||
logger.info("데이터 구조 생성 시작")
|
||||
data = {
|
||||
"timestamp": datetime.now().isoformat(),
|
||||
"system_type": "Jetson Xavier" if settings.IS_JETSON else "x86_64",
|
||||
"gpu": gpu_info,
|
||||
"system_memory": system_memory,
|
||||
"system_performance": system_performance,
|
||||
"workers": worker_status,
|
||||
"sessions": session_status,
|
||||
"jetson": jetson_info,
|
||||
"api_stats": api_stats,
|
||||
"alerts": alerts
|
||||
}
|
||||
|
||||
logger.info("히스토리에 데이터 추가 시작")
|
||||
# 히스토리에 추가
|
||||
self.history.append(data)
|
||||
if len(self.history) > self.max_history:
|
||||
self.history.pop(0)
|
||||
|
||||
logger.info("데이터 수집 완료")
|
||||
return data
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"데이터 수집 중 오류 발생: {e}")
|
||||
import traceback
|
||||
logger.error(f"상세 오류: {traceback.format_exc()}")
|
||||
|
||||
# 기본 데이터 반환
|
||||
return {
|
||||
"timestamp": datetime.now().isoformat(),
|
||||
"system_type": "Jetson Xavier" if settings.IS_JETSON else "x86_64",
|
||||
"gpu": {"total": 0, "used": 0, "free": 0, "usage_percent": 0, "utilization": 0},
|
||||
"system_memory": {"total": 0, "used": 0, "free": 0, "usage_percent": 0},
|
||||
"system_performance": {"cpu_percent": 0, "cpu_count": 1, "cpu_freq": 0},
|
||||
"workers": self._get_default_worker_status(),
|
||||
"sessions": self._get_default_session_status(),
|
||||
"jetson": {},
|
||||
"api_stats": self._get_api_statistics(),
|
||||
"alerts": [],
|
||||
"error": str(e)
|
||||
}
|
||||
|
||||
def _get_system_performance(self) -> Dict[str, Any]:
|
||||
"""시스템 성능 지표를 수집합니다."""
|
||||
|
|
@ -219,14 +286,31 @@ class MonitoringData:
|
|||
alerts = []
|
||||
current_time = datetime.now()
|
||||
|
||||
# 워커 상태 경고
|
||||
if worker_status.get("active_workers", 0) == 0:
|
||||
# 워커 상태 경고 - total_workers 필드 사용
|
||||
total_workers = worker_status.get("total_workers", 0)
|
||||
running = worker_status.get("running", False)
|
||||
|
||||
if not running:
|
||||
alerts.append({
|
||||
"level": "critical",
|
||||
"message": "워커 매니저가 중지되었습니다",
|
||||
"timestamp": current_time.isoformat(),
|
||||
"category": "workers"
|
||||
})
|
||||
elif total_workers == 0:
|
||||
alerts.append({
|
||||
"level": "critical",
|
||||
"message": "활성 워커가 없습니다",
|
||||
"timestamp": current_time.isoformat(),
|
||||
"category": "workers"
|
||||
})
|
||||
elif total_workers < 1:
|
||||
alerts.append({
|
||||
"level": "warning",
|
||||
"message": f"워커 수가 부족합니다 (현재: {total_workers}개)",
|
||||
"timestamp": current_time.isoformat(),
|
||||
"category": "workers"
|
||||
})
|
||||
|
||||
# Jetson 전용 경고
|
||||
if settings.IS_JETSON:
|
||||
|
|
@ -737,46 +821,62 @@ HTML_TEMPLATE = """
|
|||
};
|
||||
|
||||
function updateDashboard(data) {
|
||||
console.log("받은 데이터:", data); // 디버깅용 로그 추가
|
||||
|
||||
// 기본 메트릭 업데이트
|
||||
document.getElementById('system-type').textContent = data.system_type;
|
||||
document.getElementById('cpu-usage').textContent = data.system_performance?.cpu?.usage_percent?.toFixed(1) + '%' || '-';
|
||||
document.getElementById('system-memory').textContent = data.system_memory?.usage_percent?.toFixed(1) + '%' || '-';
|
||||
document.getElementById('system-type').textContent = data.system_type || '-';
|
||||
document.getElementById('cpu-usage').textContent = (data.system_performance?.cpu?.usage_percent || 0).toFixed(1) + '%';
|
||||
document.getElementById('system-memory').textContent = (data.system_memory?.usage_percent || 0).toFixed(1) + '%';
|
||||
document.getElementById('process-count').textContent = data.system_performance?.processes || '-';
|
||||
|
||||
document.getElementById('gpu-memory').textContent = data.gpu?.usage_percent?.toFixed(1) + '%' || '-';
|
||||
document.getElementById('gpu-util').textContent = data.gpu?.utilization?.toFixed(1) + '%' || '-';
|
||||
document.getElementById('gpu-temp').textContent = data.jetson?.temperature?.gpu?.toFixed(1) + '°C' || '-';
|
||||
document.getElementById('gpu-clock').textContent = data.jetson?.gpu_frequency?.toFixed(0) + 'MHz' || '-';
|
||||
// GPU 정보 업데이트
|
||||
document.getElementById('gpu-memory').textContent = (data.gpu?.usage_percent || 0).toFixed(1) + '%';
|
||||
document.getElementById('gpu-util').textContent = (data.gpu?.utilization || 0).toFixed(1) + '%';
|
||||
document.getElementById('gpu-temp').textContent = (data.jetson?.temperature?.gpu || 0).toFixed(1) + '°C';
|
||||
document.getElementById('gpu-clock').textContent = (data.jetson?.gpu_frequency || 0).toFixed(0) + 'MHz';
|
||||
|
||||
document.getElementById('worker-count').textContent = data.workers?.active_workers || '-';
|
||||
document.getElementById('session-pool').textContent = data.sessions?.total_sessions || '-';
|
||||
document.getElementById('queue-size').textContent = data.workers?.queue_size || '-';
|
||||
document.getElementById('worker-status').textContent = data.workers?.status || '-';
|
||||
// 워커 정보 업데이트
|
||||
if (data.workers) {
|
||||
document.getElementById('worker-count').textContent = data.workers.total_workers || 0;
|
||||
document.getElementById('session-pool').textContent = Object.keys(data.sessions || {}).length;
|
||||
document.getElementById('queue-size').textContent = data.workers.queue_size || 0;
|
||||
document.getElementById('worker-status').textContent = data.workers.running ? '실행 중' : '중지됨';
|
||||
|
||||
// 워커 상태별 상세 정보 표시 (디버깅용)
|
||||
console.log("워커 상태:", data.workers);
|
||||
if (data.workers.workers_by_status) {
|
||||
const idleCount = data.workers.workers_by_status.idle?.length || 0;
|
||||
const busyCount = data.workers.workers_by_status.busy?.length || 0;
|
||||
console.log(`유휴 워커: ${idleCount}, 작업 중 워커: ${busyCount}`);
|
||||
}
|
||||
}
|
||||
|
||||
// API 통계 업데이트
|
||||
document.getElementById('total-requests').textContent = data.api_stats?.total_requests || '-';
|
||||
document.getElementById('success-rate').textContent = (data.api_stats?.success_rate?.toFixed(1) || '0') + '%';
|
||||
document.getElementById('avg-response-time').textContent = (data.api_stats?.average_response_time?.toFixed(2) || '0') + 'ms';
|
||||
document.getElementById('error-count').textContent = data.api_stats?.failed_requests || '-';
|
||||
if (data.api_stats) {
|
||||
document.getElementById('total-requests').textContent = data.api_stats.total_requests || 0;
|
||||
document.getElementById('success-rate').textContent = (data.api_stats.success_rate || 0).toFixed(1) + '%';
|
||||
document.getElementById('avg-response-time').textContent = (data.api_stats.average_response_time || 0).toFixed(2) + 'ms';
|
||||
document.getElementById('error-count').textContent = data.api_stats.failed_requests || 0;
|
||||
}
|
||||
|
||||
// 시스템 성능 상세 업데이트
|
||||
if (data.system_performance?.cpu) {
|
||||
document.getElementById('cpu-count').textContent = data.system_performance.cpu.count || '-';
|
||||
document.getElementById('cpu-freq').textContent = (data.system_performance.cpu.frequency_mhz?.toFixed(0) || '0') + 'MHz';
|
||||
document.getElementById('load-1min').textContent = data.system_performance.cpu.load_average?.toFixed(2) || '-';
|
||||
document.getElementById('load-5min').textContent = data.system_performance.cpu.load_average?.toFixed(2) || '-';
|
||||
document.getElementById('cpu-freq').textContent = (data.system_performance.cpu.frequency_mhz || 0).toFixed(0) + 'MHz';
|
||||
document.getElementById('load-1min').textContent = (data.system_performance.cpu.load_average?.['1min'] || 0).toFixed(2);
|
||||
document.getElementById('load-5min').textContent = (data.system_performance.cpu.load_average?.['5min'] || 0).toFixed(2);
|
||||
}
|
||||
|
||||
if (data.system_performance?.disk) {
|
||||
document.getElementById('disk-read').textContent = (data.system_performance.disk.read_bytes / 1024 / 1024).toFixed(2);
|
||||
document.getElementById('disk-write').textContent = (data.system_performance.disk.write_bytes / 1024 / 1024).toFixed(2);
|
||||
document.getElementById('disk-read').textContent = ((data.system_performance.disk.read_bytes || 0) / 1024 / 1024).toFixed(2);
|
||||
document.getElementById('disk-write').textContent = ((data.system_performance.disk.write_bytes || 0) / 1024 / 1024).toFixed(2);
|
||||
document.getElementById('disk-read-count').textContent = data.system_performance.disk.read_count || '-';
|
||||
document.getElementById('disk-write-count').textContent = data.system_performance.disk.write_count || '-';
|
||||
}
|
||||
|
||||
if (data.system_performance?.network) {
|
||||
document.getElementById('net-sent').textContent = (data.system_performance.network.bytes_sent / 1024 / 1024).toFixed(2);
|
||||
document.getElementById('net-recv').textContent = (data.system_performance.network.bytes_recv / 1024 / 1024).toFixed(2);
|
||||
document.getElementById('net-sent').textContent = ((data.system_performance.network.bytes_sent || 0) / 1024 / 1024).toFixed(2);
|
||||
document.getElementById('net-recv').textContent = ((data.system_performance.network.bytes_recv || 0) / 1024 / 1024).toFixed(2);
|
||||
document.getElementById('net-sent-pkts').textContent = data.system_performance.network.packets_sent || '-';
|
||||
document.getElementById('net-recv-pkts').textContent = data.system_performance.network.packets_recv || '-';
|
||||
}
|
||||
|
|
@ -877,20 +977,50 @@ async def get_status():
|
|||
"""실시간 서버 상태 데이터를 반환합니다."""
|
||||
return await monitoring_data.collect_data()
|
||||
|
||||
# 간단한 상태 엔드포인트
|
||||
@api_router.get("/simple")
|
||||
def get_simple_status():
|
||||
"""간단한 상태 정보를 반환합니다."""
|
||||
async def get_simple_status():
|
||||
"""간단한 서버 상태를 반환합니다."""
|
||||
try:
|
||||
import psutil
|
||||
# status.json 파일에서 상태 읽기
|
||||
status = read_status_from_file()
|
||||
|
||||
# 시스템 메모리 정보
|
||||
memory_info = gpu_monitor.get_system_memory_info()
|
||||
|
||||
return {
|
||||
"timestamp": time.time(),
|
||||
"system_type": "Jetson Xavier" if settings.IS_JETSON else "x86_64",
|
||||
"cpu_percent": psutil.cpu_percent(),
|
||||
"memory_percent": psutil.virtual_memory().percent,
|
||||
"cpu_percent": psutil.cpu_percent(interval=0.1),
|
||||
"memory_percent": memory_info.get("usage_percent", 0),
|
||||
"status": "running"
|
||||
}
|
||||
except Exception as e:
|
||||
return {"error": f"간단한 상태 수집 실패: {str(e)}"}
|
||||
logger.error(f"간단한 상태 조회 실패: {e}")
|
||||
return {"error": str(e)}
|
||||
|
||||
# 테스트용 엔드포인트
|
||||
@api_router.get("/test")
|
||||
async def test_endpoint():
|
||||
"""테스트용 엔드포인트입니다."""
|
||||
try:
|
||||
# status.json 파일 읽기 테스트
|
||||
status = read_status_from_file()
|
||||
|
||||
# GPU 모니터 테스트
|
||||
gpu_memory = gpu_monitor.get_gpu_memory_info()
|
||||
system_memory = gpu_monitor.get_system_memory_info()
|
||||
|
||||
return {
|
||||
"message": "테스트 성공",
|
||||
"status_file": "읽기 성공" if status else "읽기 실패",
|
||||
"gpu_memory": gpu_memory,
|
||||
"system_memory": system_memory,
|
||||
"timestamp": time.time()
|
||||
}
|
||||
except Exception as e:
|
||||
logger.error(f"테스트 엔드포인트 실패: {e}")
|
||||
return {"error": str(e)}
|
||||
|
||||
@api_router.get("/test_data")
|
||||
async def get_test_data():
|
||||
|
|
|
|||
14579
logs/main_server.log
14579
logs/main_server.log
File diff suppressed because it is too large
Load Diff
|
|
@ -1 +1 @@
|
|||
396102
|
||||
406750
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -1 +1 @@
|
|||
396175
|
||||
406772
|
||||
|
|
|
|||
161
main.py
161
main.py
|
|
@ -31,17 +31,38 @@ start_time = time.time()
|
|||
|
||||
async def save_status_periodically():
|
||||
"""주기적으로 워커와 세션 상태를 파일에 저장합니다."""
|
||||
logger.info("🔄 상태 저장 백그라운드 작업 시작됨")
|
||||
iteration = 0
|
||||
while True:
|
||||
try:
|
||||
iteration += 1
|
||||
logger.debug(f"상태 저장 시도 #{iteration}")
|
||||
|
||||
# 워커 상태 수집
|
||||
worker_status = worker_manager.get_status()
|
||||
logger.debug(f"워커 상태 수집 완료: {worker_status}")
|
||||
|
||||
# 세션 상태 수집
|
||||
session_status = session_pool.get_status()
|
||||
logger.debug(f"세션 상태 수집 완료: {session_status}")
|
||||
|
||||
status = {
|
||||
"worker_status": worker_manager.get_status(),
|
||||
"session_status": session_pool.get_status(),
|
||||
"worker_status": worker_status,
|
||||
"session_status": session_status,
|
||||
"timestamp": time.time()
|
||||
}
|
||||
|
||||
# 파일에 저장
|
||||
with open("status.json", "w") as f:
|
||||
json.dump(status, f)
|
||||
json.dump(status, f, indent=2)
|
||||
|
||||
logger.info(f"상태 저장 완료 #{iteration}: {time.strftime('%H:%M:%S')}")
|
||||
|
||||
except Exception as e:
|
||||
logger.warning(f"상태 저장 실패: {e}")
|
||||
logger.error(f"상태 저장 실패 #{iteration}: {e}")
|
||||
import traceback
|
||||
logger.error(f"상세 오류: {traceback.format_exc()}")
|
||||
|
||||
await asyncio.sleep(1)
|
||||
|
||||
@asynccontextmanager
|
||||
|
|
@ -56,7 +77,9 @@ async def lifespan(app: FastAPI):
|
|||
logger.info("✅ 공유 객체를 app.state에 저장 완료")
|
||||
|
||||
# 상태 저장 백그라운드 작업 시작
|
||||
logger.info("🔄 상태 저장 백그라운드 작업 생성 중...")
|
||||
status_task = asyncio.create_task(save_status_periodically())
|
||||
logger.info("✅ 상태 저장 백그라운드 작업 생성 완료")
|
||||
|
||||
try:
|
||||
# 세션 풀 초기화
|
||||
|
|
@ -116,134 +139,8 @@ app.include_router(router)
|
|||
# app.mount("/monitoring", monitor_app, name="monitoring")
|
||||
|
||||
|
||||
# 모니터링 데이터 직접 제공 (완전 통합)
|
||||
@app.get("/monitoring/api/status")
|
||||
async def get_monitoring_status():
|
||||
"""모니터링 상태를 직접 반환합니다."""
|
||||
try:
|
||||
import psutil
|
||||
from app.utils.gpu_monitor import GPUMonitor
|
||||
|
||||
# 시스템 정보 수집
|
||||
cpu_percent = psutil.cpu_percent()
|
||||
memory = psutil.virtual_memory()
|
||||
process_count = len(psutil.pids())
|
||||
|
||||
# GPU 정보 수집
|
||||
gpu_monitor = GPUMonitor()
|
||||
gpu_info = await gpu_monitor.get_gpu_info()
|
||||
|
||||
# 워커 매니저 상태 (안전한 방식)
|
||||
try:
|
||||
worker_status = {
|
||||
"total_workers": getattr(worker_manager, 'workers', {}).__len__() if hasattr(worker_manager, 'workers') else 0,
|
||||
"queue_size": getattr(worker_manager, 'queue', None).qsize() if hasattr(worker_manager, 'queue') else 0,
|
||||
"workers_by_status": {"idle": [], "busy": [], "starting": [], "stopping": [], "error": []},
|
||||
"running": getattr(worker_manager, 'running', False)
|
||||
}
|
||||
except Exception as e:
|
||||
logger.warning(f"워커 상태 수집 실패: {e}")
|
||||
worker_status = {
|
||||
"total_workers": 0,
|
||||
"queue_size": 0,
|
||||
"workers_by_status": {"idle": [], "busy": [], "starting": [], "stopping": [], "error": []},
|
||||
"running": False
|
||||
}
|
||||
|
||||
# 세션 풀 상태 (안전한 방식)
|
||||
try:
|
||||
sessions = getattr(session_pool, 'sessions', {})
|
||||
total_sessions = len(sessions) if sessions else 0
|
||||
available_sessions = len([s for s in sessions.values() if getattr(s, 'available', False)]) if sessions else 0
|
||||
|
||||
session_status = {
|
||||
"total_sessions": total_sessions,
|
||||
"available_sessions": available_sessions,
|
||||
"model_distribution": {"simple-lama": 0, "migan": 0, "rembg": 0}
|
||||
}
|
||||
except Exception as e:
|
||||
logger.warning(f"세션 상태 수집 실패: {e}")
|
||||
session_status = {
|
||||
"total_sessions": 0,
|
||||
"available_sessions": 0,
|
||||
"model_distribution": {"simple-lama": 0, "migan": 0, "rembg": 0}
|
||||
}
|
||||
|
||||
# API 통계 (간단한 버전)
|
||||
api_stats = {
|
||||
"total_requests": 0, # 실제로는 카운터가 필요
|
||||
"success_rate": 100.0,
|
||||
"average_response_time": 0.0,
|
||||
"error_count": 0
|
||||
}
|
||||
|
||||
return {
|
||||
"timestamp": time.time(),
|
||||
"system_type": "Jetson Xavier" if settings.IS_JETSON else "x86_64",
|
||||
"system": {
|
||||
"cpu_percent": cpu_percent,
|
||||
"memory_percent": memory.percent,
|
||||
"process_count": process_count
|
||||
},
|
||||
"gpu": gpu_info,
|
||||
"worker_status": worker_status,
|
||||
"session_status": session_status,
|
||||
"api_stats": api_stats
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"모니터링 데이터 수집 실패: {e}")
|
||||
return {"error": f"모니터링 데이터 수집 실패: {str(e)}"}
|
||||
|
||||
@app.get("/monitoring/api/simple")
|
||||
async def get_simple_monitoring():
|
||||
"""간단한 모니터링 상태를 반환합니다."""
|
||||
try:
|
||||
import psutil
|
||||
return {
|
||||
"timestamp": time.time(),
|
||||
"system_type": "Jetson Xavier" if settings.IS_JETSON else "x86_64",
|
||||
"cpu_percent": psutil.cpu_percent(),
|
||||
"memory_percent": psutil.virtual_memory().percent,
|
||||
"status": "running"
|
||||
}
|
||||
except Exception as e:
|
||||
return {"error": f"간단한 상태 수집 실패: {str(e)}"}
|
||||
|
||||
@app.get("/monitoring/api/worker-status")
|
||||
async def get_worker_status():
|
||||
"""워커 상태를 반환합니다."""
|
||||
try:
|
||||
workers = getattr(worker_manager, 'workers', {})
|
||||
queue = getattr(worker_manager, 'queue', None)
|
||||
running = getattr(worker_manager, 'running', False)
|
||||
|
||||
return {
|
||||
"total_workers": len(workers) if workers else 0,
|
||||
"queue_size": queue.qsize() if queue else 0,
|
||||
"running": running,
|
||||
"status": "active" if running else "stopped"
|
||||
}
|
||||
except Exception as e:
|
||||
logger.warning(f"워커 상태 조회 실패: {e}")
|
||||
return {"error": f"워커 상태 조회 실패: {str(e)}"}
|
||||
|
||||
@app.get("/monitoring/api/session-status")
|
||||
async def get_session_status():
|
||||
"""세션 풀 상태를 반환합니다."""
|
||||
try:
|
||||
sessions = getattr(session_pool, 'sessions', {})
|
||||
total_sessions = len(sessions) if sessions else 0
|
||||
available_sessions = len([s for s in sessions.values() if getattr(s, 'available', False)]) if sessions else 0
|
||||
|
||||
return {
|
||||
"total_sessions": total_sessions,
|
||||
"available_sessions": available_sessions,
|
||||
"model_distribution": {"simple-lama": 0, "migan": 0, "rembg": 0}
|
||||
}
|
||||
except Exception as e:
|
||||
logger.warning(f"세션 상태 조회 실패: {e}")
|
||||
return {"error": f"세션 상태 조회 실패: {str(e)}"}
|
||||
# 모니터링은 독립적인 서버(포트 8001)에서 처리됩니다.
|
||||
# status.json 파일을 통해 데이터를 공유합니다.
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
|
|||
|
|
@ -379,11 +379,10 @@ start_servers() {
|
|||
--daemon \
|
||||
--pid "$LOG_DIR/main_server.pid"
|
||||
else
|
||||
# 개발 모드: Uvicorn 사용
|
||||
# 개발 모드: Uvicorn 사용 (단일 프로세스로 실행하여 lifespan 함수가 정상 작동하도록 함)
|
||||
nohup python -m uvicorn main:app \
|
||||
--host 0.0.0.0 \
|
||||
--port $MAIN_SERVER_PORT \
|
||||
--workers $WORKERS \
|
||||
--log-level info \
|
||||
--access-log \
|
||||
> "$LOG_DIR/main_server.log" 2>&1 &
|
||||
|
|
|
|||
41
status.json
41
status.json
|
|
@ -1 +1,40 @@
|
|||
{"worker_status": {"running": true, "total_workers": 1, "queue_size": 0, "workers_by_status": {"idle": [{"id": "worker_d5e6bd0a", "status": "idle", "task_count": 0, "error_count": 0, "last_task_at": null}], "busy": [], "starting": [], "stopping": [], "error": []}}, "session_status": {"simple_lama": {"total": 2, "in_use": 0, "available": 2}, "migan": {"total": 2, "in_use": 0, "available": 2}, "rembg": {"total": 1, "in_use": 0, "available": 1}}, "api_stats": {"total_requests": 3, "successful_requests": 2, "failed_requests": 1, "endpoint_usage": {"/": 1, "/health": 1, "/api/v1/model": 1}, "start_time": 1756296888.0927753, "uptime_seconds": 253.7529318332672, "average_response_time_ms": 2.473115921020508}, "timestamp": 1756297141.8458862}
|
||||
{
|
||||
"worker_status": {
|
||||
"running": true,
|
||||
"total_workers": 1,
|
||||
"queue_size": 0,
|
||||
"workers_by_status": {
|
||||
"idle": [
|
||||
{
|
||||
"id": "worker_0d4f9bb0",
|
||||
"status": "idle",
|
||||
"task_count": 0,
|
||||
"error_count": 0,
|
||||
"last_task_at": null
|
||||
}
|
||||
],
|
||||
"busy": [],
|
||||
"starting": [],
|
||||
"stopping": [],
|
||||
"error": []
|
||||
}
|
||||
},
|
||||
"session_status": {
|
||||
"simple_lama": {
|
||||
"total": 2,
|
||||
"in_use": 0,
|
||||
"available": 2
|
||||
},
|
||||
"migan": {
|
||||
"total": 2,
|
||||
"in_use": 0,
|
||||
"available": 2
|
||||
},
|
||||
"rembg": {
|
||||
"total": 1,
|
||||
"in_use": 0,
|
||||
"available": 1
|
||||
}
|
||||
},
|
||||
"timestamp": 1756313157.7682712
|
||||
}
|
||||
Loading…
Reference in New Issue