상태 JSON 파일의 요청 통계 및 가동 시간을 업데이트하고, 새로운 모델 정보 API를 추가하였습니다. 모니터링 포트를 8080에서 8888로 변경하였으며, .gitignore 파일에 로그 파일 패턴을 추가하였습니다. 스크립트에서 가상환경 경로 자동 감지 기능을 개선하였습니다.
This commit is contained in:
parent
0cb1596be1
commit
29f0de65e1
|
|
@ -1,3 +1,4 @@
|
||||||
|
logs/
|
||||||
bin/
|
bin/
|
||||||
include/
|
include/
|
||||||
lib/
|
lib/
|
||||||
|
|
@ -17,6 +18,7 @@ pyvenv.cfg
|
||||||
*.pyzw
|
*.pyzw
|
||||||
*.pyzwz
|
*.pyzwz
|
||||||
*.pyzwzw
|
*.pyzwzw
|
||||||
logs/
|
|
||||||
status.json
|
status.json
|
||||||
|
*.logs
|
||||||
|
*.log
|
||||||
|
*.pid
|
||||||
|
|
@ -352,6 +352,47 @@ async def remove_background(
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/api/v1/model")
|
||||||
|
async def get_model_info():
|
||||||
|
"""모델 정보 반환 (클라이언트 헬스체크 호환)"""
|
||||||
|
try:
|
||||||
|
# 현재 사용 가능한 모델 목록
|
||||||
|
models = [
|
||||||
|
{
|
||||||
|
"name": "simple-lama",
|
||||||
|
"type": "inpainting",
|
||||||
|
"status": "available",
|
||||||
|
"description": "Simple LAMA 인페인팅 모델"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "migan",
|
||||||
|
"type": "inpainting",
|
||||||
|
"status": "available",
|
||||||
|
"description": "MIGAN 인페인팅 모델"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "rembg",
|
||||||
|
"type": "rembg",
|
||||||
|
"status": "available",
|
||||||
|
"description": "Rembg 배경 제거 모델"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
return {
|
||||||
|
"success": True,
|
||||||
|
"models": models,
|
||||||
|
"server_status": "running",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"device": "cuda" if settings.IS_JETSON else "cuda",
|
||||||
|
"is_jetson": settings.IS_JETSON,
|
||||||
|
"timestamp": datetime.now().isoformat()
|
||||||
|
}
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"모델 정보 조회 실패: {e}")
|
||||||
|
raise HTTPException(status_code=500, detail=f"모델 정보 조회 실패: {str(e)}")
|
||||||
|
|
||||||
|
|
||||||
@router.get("/api/v1/samplers")
|
@router.get("/api/v1/samplers")
|
||||||
async def get_samplers():
|
async def get_samplers():
|
||||||
"""사용 가능한 샘플러 목록 반환 (iopaint 호환)"""
|
"""사용 가능한 샘플러 목록 반환 (iopaint 호환)"""
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,7 @@ class Settings(BaseSettings):
|
||||||
|
|
||||||
# Monitoring
|
# Monitoring
|
||||||
ENABLE_MONITORING: bool = True
|
ENABLE_MONITORING: bool = True
|
||||||
MONITORING_PORT: int = 8080
|
MONITORING_PORT: int = 8888
|
||||||
|
|
||||||
# Jetson performance settings
|
# Jetson performance settings
|
||||||
JETSON_GPU_FREQ: int = 1200 # MHz
|
JETSON_GPU_FREQ: int = 1200 # MHz
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ from fastapi.staticfiles import StaticFiles
|
||||||
from fastapi.responses import HTMLResponse
|
from fastapi.responses import HTMLResponse
|
||||||
import uvicorn
|
import uvicorn
|
||||||
from fastapi import APIRouter, Request
|
from fastapi import APIRouter, Request
|
||||||
|
import websockets.exceptions
|
||||||
|
|
||||||
from ..core.worker_manager import worker_manager
|
from ..core.worker_manager import worker_manager
|
||||||
from ..core.session_pool import session_pool
|
from ..core.session_pool import session_pool
|
||||||
|
|
@ -571,6 +572,50 @@ HTML_TEMPLATE = """
|
||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.status {
|
||||||
|
padding: 2px 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 0.8em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status.connected {
|
||||||
|
background-color: #d4edda;
|
||||||
|
color: #155724;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status.connecting {
|
||||||
|
background-color: #fff3cd;
|
||||||
|
color: #856404;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status.reconnecting {
|
||||||
|
background-color: #f8d7da;
|
||||||
|
color: #721c24;
|
||||||
|
animation: pulse 1s infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status.disconnected {
|
||||||
|
background-color: #f8d7da;
|
||||||
|
color: #721c24;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status.error {
|
||||||
|
background-color: #f5c6cb;
|
||||||
|
color: #491217;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status.failed {
|
||||||
|
background-color: #d1ecf1;
|
||||||
|
color: #0c5460;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes pulse {
|
||||||
|
0% { opacity: 1; }
|
||||||
|
50% { opacity: 0.5; }
|
||||||
|
100% { opacity: 1; }
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
@ -793,7 +838,8 @@ HTML_TEMPLATE = """
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="refresh-time">
|
<div class="refresh-time">
|
||||||
마지막 업데이트: <span id="last-update">-</span>
|
마지막 업데이트: <span id="last-update">-</span> |
|
||||||
|
연결 상태: <span id="connection-status" class="status connecting">연결 중...</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -852,20 +898,100 @@ HTML_TEMPLATE = """
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// WebSocket 연결
|
// WebSocket 연결 관리
|
||||||
const ws = new WebSocket(`ws://${window.location.host}/ws`);
|
let ws;
|
||||||
|
let reconnectAttempts = 0;
|
||||||
|
const maxReconnectAttempts = 5;
|
||||||
|
const reconnectInterval = 3000; // 3초
|
||||||
|
let lastHeartbeat = Date.now();
|
||||||
|
const heartbeatTimeout = 10000; // 10초 타임아웃
|
||||||
|
|
||||||
ws.onmessage = function(event) {
|
function connectWebSocket() {
|
||||||
const data = JSON.parse(event.data);
|
try {
|
||||||
updateDashboard(data);
|
ws = new WebSocket(`ws://${window.location.host}/ws`);
|
||||||
};
|
|
||||||
|
ws.onopen = function() {
|
||||||
|
console.log('WebSocket 연결이 성공했습니다.');
|
||||||
|
reconnectAttempts = 0;
|
||||||
|
// 연결 상태 표시 업데이트
|
||||||
|
document.getElementById('connection-status').textContent = '연결됨';
|
||||||
|
document.getElementById('connection-status').className = 'status connected';
|
||||||
|
};
|
||||||
|
|
||||||
|
ws.onmessage = function(event) {
|
||||||
|
try {
|
||||||
|
const data = JSON.parse(event.data);
|
||||||
|
|
||||||
|
// heartbeat 체크
|
||||||
|
if (data.heartbeat) {
|
||||||
|
lastHeartbeat = Date.now();
|
||||||
|
}
|
||||||
|
|
||||||
|
updateDashboard(data);
|
||||||
|
} catch (e) {
|
||||||
|
console.error('데이터 파싱 오류:', e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ws.onclose = function(event) {
|
||||||
|
console.log(`WebSocket 연결이 종료되었습니다. 코드: ${event.code}, 이유: ${event.reason}`);
|
||||||
|
document.getElementById('connection-status').textContent = '연결 끊어짐';
|
||||||
|
document.getElementById('connection-status').className = 'status disconnected';
|
||||||
|
|
||||||
|
// 자동 재연결 시도
|
||||||
|
if (reconnectAttempts < maxReconnectAttempts) {
|
||||||
|
reconnectAttempts++;
|
||||||
|
console.log(`재연결 시도 ${reconnectAttempts}/${maxReconnectAttempts} in ${reconnectInterval/1000}초...`);
|
||||||
|
document.getElementById('connection-status').textContent = `재연결 중... (${reconnectAttempts}/${maxReconnectAttempts})`;
|
||||||
|
document.getElementById('connection-status').className = 'status reconnecting';
|
||||||
|
|
||||||
|
setTimeout(connectWebSocket, reconnectInterval);
|
||||||
|
} else {
|
||||||
|
console.log('최대 재연결 시도 횟수를 초과했습니다. 페이지를 새로고침합니다.');
|
||||||
|
document.getElementById('connection-status').textContent = '연결 실패';
|
||||||
|
document.getElementById('connection-status').className = 'status failed';
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
location.reload();
|
||||||
|
}, 5000);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ws.onerror = function(error) {
|
||||||
|
console.error('WebSocket 오류:', error);
|
||||||
|
document.getElementById('connection-status').textContent = '연결 오류';
|
||||||
|
document.getElementById('connection-status').className = 'status error';
|
||||||
|
};
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('WebSocket 연결 생성 오류:', error);
|
||||||
|
document.getElementById('connection-status').textContent = '연결 생성 실패';
|
||||||
|
document.getElementById('connection-status').className = 'status error';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ws.onclose = function() {
|
// 페이지 가시성 변경 감지
|
||||||
console.log('WebSocket 연결이 종료되었습니다.');
|
document.addEventListener('visibilitychange', function() {
|
||||||
setTimeout(() => {
|
if (document.visibilityState === 'visible') {
|
||||||
location.reload();
|
// 탭이 다시 활성화되면 연결 상태 확인
|
||||||
}, 5000);
|
if (ws.readyState !== WebSocket.OPEN) {
|
||||||
};
|
console.log('탭이 활성화되어 WebSocket 재연결을 시도합니다.');
|
||||||
|
connectWebSocket();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 초기 연결
|
||||||
|
connectWebSocket();
|
||||||
|
|
||||||
|
// heartbeat 모니터링
|
||||||
|
setInterval(function() {
|
||||||
|
const now = Date.now();
|
||||||
|
if (now - lastHeartbeat > heartbeatTimeout && ws && ws.readyState === WebSocket.OPEN) {
|
||||||
|
console.log('heartbeat 타임아웃 - 연결을 다시 시도합니다.');
|
||||||
|
ws.close();
|
||||||
|
}
|
||||||
|
}, 5000); // 5초마다 체크
|
||||||
|
|
||||||
function updateDashboard(data) {
|
function updateDashboard(data) {
|
||||||
console.log("받은 데이터:", data); // 디버깅용 로그 추가
|
console.log("받은 데이터:", data); // 디버깅용 로그 추가
|
||||||
|
|
@ -1199,17 +1325,39 @@ async def websocket_endpoint(websocket: WebSocket):
|
||||||
"""WebSocket 연결을 처리합니다."""
|
"""WebSocket 연결을 처리합니다."""
|
||||||
await websocket.accept()
|
await websocket.accept()
|
||||||
connected_clients.append(websocket)
|
connected_clients.append(websocket)
|
||||||
|
logger.info(f"WebSocket 클라이언트 연결됨: {websocket.client}")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
while True:
|
while True:
|
||||||
# 주기적으로 데이터 전송
|
# 주기적으로 데이터 전송
|
||||||
data = await monitoring_data.collect_data()
|
data = await monitoring_data.collect_data()
|
||||||
await websocket.send_json(data)
|
|
||||||
|
# heartbeat 메시지 추가
|
||||||
|
data['heartbeat'] = time.time()
|
||||||
|
data['server_status'] = 'running'
|
||||||
|
|
||||||
|
try:
|
||||||
|
await websocket.send_json(data)
|
||||||
|
except (websockets.exceptions.ConnectionClosedOK,
|
||||||
|
websockets.exceptions.ConnectionClosedError,
|
||||||
|
RuntimeError) as e:
|
||||||
|
logger.info(f"WebSocket 연결이 끊어짐: {e}")
|
||||||
|
break
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"데이터 전송 오류: {e}")
|
||||||
|
break
|
||||||
|
|
||||||
await asyncio.sleep(2) # 2초마다 업데이트
|
await asyncio.sleep(2) # 2초마다 업데이트
|
||||||
|
|
||||||
except WebSocketDisconnect:
|
except WebSocketDisconnect:
|
||||||
connected_clients.remove(websocket)
|
logger.info("클라이언트가 연결을 끊음")
|
||||||
logger.info("클라이언트 연결 해제")
|
except Exception as e:
|
||||||
|
logger.error(f"WebSocket 오류: {e}")
|
||||||
|
finally:
|
||||||
|
# 연결된 클라이언트 목록에서 제거
|
||||||
|
if websocket in connected_clients:
|
||||||
|
connected_clients.remove(websocket)
|
||||||
|
logger.info(f"WebSocket 클라이언트 연결 해제됨: {websocket.client}")
|
||||||
|
|
||||||
|
|
||||||
async def broadcast_data():
|
async def broadcast_data():
|
||||||
|
|
@ -1225,7 +1373,11 @@ async def broadcast_data():
|
||||||
for client in connected_clients:
|
for client in connected_clients:
|
||||||
try:
|
try:
|
||||||
await client.send_text(message)
|
await client.send_text(message)
|
||||||
except Exception:
|
except (websockets.exceptions.ConnectionClosedOK,
|
||||||
|
websockets.exceptions.ConnectionClosedError,
|
||||||
|
RuntimeError,
|
||||||
|
Exception) as e:
|
||||||
|
logger.debug(f"브로드캐스트 중 클라이언트 연결 끊어짐: {e}")
|
||||||
disconnected.append(client)
|
disconnected.append(client)
|
||||||
|
|
||||||
for client in disconnected:
|
for client in disconnected:
|
||||||
|
|
|
||||||
1729
logs/main_server.log
1729
logs/main_server.log
File diff suppressed because it is too large
Load Diff
|
|
@ -1 +1 @@
|
||||||
1312285
|
1328887
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,6 @@
|
||||||
INFO: Started server process [1312316]
|
INFO: Started server process [1328918]
|
||||||
INFO: Waiting for application startup.
|
INFO: Waiting for application startup.
|
||||||
Fan control not available
|
Fan control not available
|
||||||
INFO: Application startup complete.
|
INFO: Application startup complete.
|
||||||
INFO: Uvicorn running on http://0.0.0.0:8080 (Press CTRL+C to quit)
|
INFO: Uvicorn running on http://0.0.0.0:8888 (Press CTRL+C to quit)
|
||||||
INFO: 127.0.0.1:35348 - "GET /api/simple HTTP/1.1" 200 OK
|
INFO: 127.0.0.1:42340 - "GET /api/simple HTTP/1.1" 200 OK
|
||||||
INFO: 127.0.0.1:37590 - "GET / HTTP/1.1" 200 OK
|
|
||||||
INFO: 127.0.0.1:37590 - "GET /favicon.ico HTTP/1.1" 404 Not Found
|
|
||||||
INFO: ('127.0.0.1', 37632) - "WebSocket /ws" [accepted]
|
|
||||||
INFO: connection open
|
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
1312316
|
1328918
|
||||||
|
|
|
||||||
|
|
@ -30,9 +30,44 @@ log_error() {
|
||||||
echo -e "${RED}[ERROR]${NC} $1"
|
echo -e "${RED}[ERROR]${NC} $1"
|
||||||
}
|
}
|
||||||
|
|
||||||
# 기본 설정
|
# 기본 설정 - 동적 경로 처리
|
||||||
PROJECT_ROOT="/home/ckh08045/work/inpaintServer"
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
VENV_PATH="$PROJECT_ROOT"
|
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
|
||||||
|
|
||||||
|
# 가상환경 경로 자동 감지
|
||||||
|
detect_venv_path() {
|
||||||
|
local possible_paths=(
|
||||||
|
"$PROJECT_ROOT/venv" # 일반적인 venv 경로
|
||||||
|
"$PROJECT_ROOT/.venv" # 숨김 venv 경로
|
||||||
|
"$PROJECT_ROOT/env" # env 경로
|
||||||
|
"$PROJECT_ROOT/.env" # 숨김 env 경로
|
||||||
|
"$PROJECT_ROOT" # 프로젝트 루트 (Jetson 방식)
|
||||||
|
)
|
||||||
|
|
||||||
|
for path in "${possible_paths[@]}"; do
|
||||||
|
if [ -f "$path/bin/python" ] || [ -f "$path/bin/python3" ]; then
|
||||||
|
VENV_PATH="$path"
|
||||||
|
log_info "가상환경 경로 감지: $VENV_PATH"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# 현재 활성화된 가상환경 확인
|
||||||
|
if [ -n "$VIRTUAL_ENV" ]; then
|
||||||
|
VENV_PATH="$VIRTUAL_ENV"
|
||||||
|
log_info "활성화된 가상환경 사용: $VENV_PATH"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 가상환경을 찾지 못한 경우 시스템별 기본값 사용
|
||||||
|
if [ "$(uname -m)" = "aarch64" ] && uname -a | grep -q "tegra"; then
|
||||||
|
VENV_PATH="$PROJECT_ROOT"
|
||||||
|
log_warning "가상환경을 찾을 수 없어 Jetson 기본값 사용: $VENV_PATH"
|
||||||
|
else
|
||||||
|
VENV_PATH="$PROJECT_ROOT/venv"
|
||||||
|
log_warning "가상환경을 찾을 수 없어 x86 기본값 사용: $VENV_PATH"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
# 시스템 감지
|
# 시스템 감지
|
||||||
detect_system() {
|
detect_system() {
|
||||||
|
|
@ -687,6 +722,7 @@ main() {
|
||||||
log_info "인페인팅 서버 의존성 설치 시작"
|
log_info "인페인팅 서버 의존성 설치 시작"
|
||||||
|
|
||||||
detect_system
|
detect_system
|
||||||
|
detect_venv_path
|
||||||
check_system_requirements
|
check_system_requirements
|
||||||
check_cuda_installation
|
check_cuda_installation
|
||||||
activate_venv
|
activate_venv
|
||||||
|
|
|
||||||
|
|
@ -176,8 +176,14 @@ install_dependencies() {
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# requirements.txt 확인
|
# requirements.txt 확인
|
||||||
if [ ! -f "requirements.txt" ]; then
|
if [ "$SYSTEM_TYPE" = "x86" ]; then
|
||||||
log_error "requirements.txt 파일을 찾을 수 없습니다"
|
REQ_FILE="requirements_x86.txt"
|
||||||
|
else
|
||||||
|
REQ_FILE="requirements.txt"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -f "$REQ_FILE" ]; then
|
||||||
|
log_error "$REQ_FILE 파일을 찾을 수 없습니다"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
@ -188,7 +194,7 @@ install_dependencies() {
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# 기본 패키지 설치
|
# 기본 패키지 설치
|
||||||
pip install -r requirements.txt
|
pip install -r "$REQ_FILE"
|
||||||
|
|
||||||
log_success "의존성 설치 완료"
|
log_success "의존성 설치 완료"
|
||||||
}
|
}
|
||||||
|
|
@ -282,7 +288,7 @@ print_completion_info() {
|
||||||
echo "🚀 서버 접속 정보:"
|
echo "🚀 서버 접속 정보:"
|
||||||
echo " - 메인 API 서버: http://localhost:8008"
|
echo " - 메인 API 서버: http://localhost:8008"
|
||||||
echo " - API 문서: http://localhost:8008/docs"
|
echo " - API 문서: http://localhost:8008/docs"
|
||||||
echo " - 모니터링 대시보드: http://localhost:8080"
|
echo " - 모니터링 대시보드: http://localhost:8888"
|
||||||
echo " - 헬스 체크: http://localhost:8008/health"
|
echo " - 헬스 체크: http://localhost:8008/health"
|
||||||
else
|
else
|
||||||
echo ""
|
echo ""
|
||||||
|
|
|
||||||
|
|
@ -141,7 +141,7 @@ echo " bash scripts/setup_and_run.sh"
|
||||||
echo ""
|
echo ""
|
||||||
log_info "서버 포트:"
|
log_info "서버 포트:"
|
||||||
echo " 메인 서버: http://localhost:8008"
|
echo " 메인 서버: http://localhost:8008"
|
||||||
echo " 모니터링: http://localhost:8080"
|
echo " 모니터링: http://localhost:8888"
|
||||||
echo ""
|
echo ""
|
||||||
log_info "GPU 설정:"
|
log_info "GPU 설정:"
|
||||||
echo " RTX 3060 12GB 권장 설정이 자동으로 적용됩니다"
|
echo " RTX 3060 12GB 권장 설정이 자동으로 적용됩니다"
|
||||||
|
|
|
||||||
|
|
@ -30,13 +30,48 @@ log_error() {
|
||||||
echo -e "${RED}[ERROR]${NC} $1"
|
echo -e "${RED}[ERROR]${NC} $1"
|
||||||
}
|
}
|
||||||
|
|
||||||
# 기본 설정
|
# 기본 설정 - 동적 경로 처리
|
||||||
PROJECT_ROOT="/home/ckh08045/work/inpaintServer"
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
VENV_PATH="$PROJECT_ROOT"
|
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
|
||||||
MAIN_SERVER_PORT=8008
|
MAIN_SERVER_PORT=8008
|
||||||
MONITORING_PORT=8080
|
MONITORING_PORT=8888
|
||||||
LOG_DIR="$PROJECT_ROOT/logs"
|
LOG_DIR="$PROJECT_ROOT/logs"
|
||||||
|
|
||||||
|
# 가상환경 경로 자동 감지
|
||||||
|
detect_venv_path() {
|
||||||
|
local possible_paths=(
|
||||||
|
"$PROJECT_ROOT/venv" # 일반적인 venv 경로
|
||||||
|
"$PROJECT_ROOT/.venv" # 숨김 venv 경로
|
||||||
|
"$PROJECT_ROOT/env" # env 경로
|
||||||
|
"$PROJECT_ROOT/.env" # 숨김 env 경로
|
||||||
|
"$PROJECT_ROOT" # 프로젝트 루트 (Jetson 방식)
|
||||||
|
)
|
||||||
|
|
||||||
|
for path in "${possible_paths[@]}"; do
|
||||||
|
if [ -f "$path/bin/python" ] || [ -f "$path/bin/python3" ]; then
|
||||||
|
VENV_PATH="$path"
|
||||||
|
log_info "가상환경 경로 감지: $VENV_PATH"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# 현재 활성화된 가상환경 확인
|
||||||
|
if [ -n "$VIRTUAL_ENV" ]; then
|
||||||
|
VENV_PATH="$VIRTUAL_ENV"
|
||||||
|
log_info "활성화된 가상환경 사용: $VENV_PATH"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 가상환경을 찾지 못한 경우 시스템별 기본값 사용
|
||||||
|
if [ "$(uname -m)" = "aarch64" ] && uname -a | grep -q "tegra"; then
|
||||||
|
VENV_PATH="$PROJECT_ROOT"
|
||||||
|
log_warning "가상환경을 찾을 수 없어 Jetson 기본값 사용: $VENV_PATH"
|
||||||
|
else
|
||||||
|
VENV_PATH="$PROJECT_ROOT/venv"
|
||||||
|
log_warning "가상환경을 찾을 수 없어 x86 기본값 사용: $VENV_PATH"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
# 시스템 감지
|
# 시스템 감지
|
||||||
detect_system() {
|
detect_system() {
|
||||||
if [ "$(uname -m)" = "aarch64" ] && uname -a | grep -q "tegra"; then
|
if [ "$(uname -m)" = "aarch64" ] && uname -a | grep -q "tegra"; then
|
||||||
|
|
@ -129,6 +164,7 @@ check_environment() {
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# 가상환경 확인
|
# 가상환경 확인
|
||||||
|
log_info "가상환경 경로 확인: $VENV_PATH"
|
||||||
if [ ! -f "$VENV_PATH/bin/activate" ]; then
|
if [ ! -f "$VENV_PATH/bin/activate" ]; then
|
||||||
log_error "가상환경을 찾을 수 없습니다: $VENV_PATH"
|
log_error "가상환경을 찾을 수 없습니다: $VENV_PATH"
|
||||||
exit 1
|
exit 1
|
||||||
|
|
@ -506,6 +542,7 @@ main() {
|
||||||
log_info "인페인팅 서버 시작 스크립트 실행"
|
log_info "인페인팅 서버 시작 스크립트 실행"
|
||||||
|
|
||||||
detect_system
|
detect_system
|
||||||
|
detect_venv_path
|
||||||
check_environment
|
check_environment
|
||||||
activate_venv
|
activate_venv
|
||||||
check_ports
|
check_ports
|
||||||
|
|
|
||||||
|
|
@ -30,11 +30,12 @@ log_error() {
|
||||||
echo -e "${RED}[ERROR]${NC} $1"
|
echo -e "${RED}[ERROR]${NC} $1"
|
||||||
}
|
}
|
||||||
|
|
||||||
# 기본 설정
|
# 기본 설정 - 동적 경로 처리
|
||||||
PROJECT_ROOT="/home/ckh08045/work/inpaintServer"
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
|
||||||
LOG_DIR="$PROJECT_ROOT/logs"
|
LOG_DIR="$PROJECT_ROOT/logs"
|
||||||
MAIN_PORT=8008
|
MAIN_PORT=8008
|
||||||
MONITORING_PORT=8080
|
MONITORING_PORT=8888
|
||||||
|
|
||||||
# 옵션 파싱
|
# 옵션 파싱
|
||||||
DETAILED=false
|
DETAILED=false
|
||||||
|
|
|
||||||
|
|
@ -29,8 +29,9 @@ log_error() {
|
||||||
echo -e "${RED}[ERROR]${NC} $1"
|
echo -e "${RED}[ERROR]${NC} $1"
|
||||||
}
|
}
|
||||||
|
|
||||||
# 기본 설정
|
# 기본 설정 - 동적 경로 처리
|
||||||
PROJECT_ROOT="/home/ckh08045/work/inpaintServer"
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
|
||||||
PID_FILE="$PROJECT_ROOT/server.pid"
|
PID_FILE="$PROJECT_ROOT/server.pid"
|
||||||
LOG_FILE="$PROJECT_ROOT/logs/server.log"
|
LOG_FILE="$PROJECT_ROOT/logs/server.log"
|
||||||
|
|
||||||
|
|
@ -70,8 +71,8 @@ find_server_processes() {
|
||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# 포트 8080에서 실행 중인 프로세스 찾기 (모니터링)
|
# 포트 8888에서 실행 중인 프로세스 찾기 (모니터링)
|
||||||
local monitor_processes=$(lsof -ti:8080 2>/dev/null || echo "")
|
local monitor_processes=$(lsof -ti:8888 2>/dev/null || echo "")
|
||||||
if [ -n "$monitor_processes" ]; then
|
if [ -n "$monitor_processes" ]; then
|
||||||
for pid in $monitor_processes; do
|
for pid in $monitor_processes; do
|
||||||
if [[ ! " ${processes[@]} " =~ " ${pid} " ]]; then
|
if [[ ! " ${processes[@]} " =~ " ${pid} " ]]; then
|
||||||
|
|
@ -135,10 +136,10 @@ stop_server() {
|
||||||
log_success "포트 8008 해제됨"
|
log_success "포트 8008 해제됨"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if lsof -ti:8080 > /dev/null 2>&1; then
|
if lsof -ti:8888 > /dev/null 2>&1; then
|
||||||
log_warning "포트 8080이 여전히 사용 중입니다"
|
log_warning "포트 8888이 여전히 사용 중입니다"
|
||||||
else
|
else
|
||||||
log_success "포트 8080 해제됨"
|
log_success "포트 8888 해제됨"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
log_success "서버 정지 완료"
|
log_success "서버 정지 완료"
|
||||||
|
|
@ -165,10 +166,10 @@ force_stop() {
|
||||||
kill -KILL $port_8008 2>/dev/null || true
|
kill -KILL $port_8008 2>/dev/null || true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
local port_8080=$(lsof -ti:8080 2>/dev/null || echo "")
|
local port_8888=$(lsof -ti:8888 2>/dev/null || echo "")
|
||||||
if [ -n "$port_8080" ]; then
|
if [ -n "$port_8888" ]; then
|
||||||
log_info "포트 8080 사용 프로세스 강제 종료 중..."
|
log_info "포트 8888 사용 프로세스 강제 종료 중..."
|
||||||
kill -KILL $port_8080 2>/dev/null || true
|
kill -KILL $port_8888 2>/dev/null || true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# PID 파일 정리
|
# PID 파일 정리
|
||||||
|
|
@ -204,10 +205,10 @@ check_status() {
|
||||||
log_info "포트 8008: 사용 안함"
|
log_info "포트 8008: 사용 안함"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if lsof -ti:8080 > /dev/null 2>&1; then
|
if lsof -ti:8888 > /dev/null 2>&1; then
|
||||||
log_info "포트 8080: 사용 중 (모니터링 대시보드)"
|
log_info "포트 8888: 사용 중 (모니터링 대시보드)"
|
||||||
else
|
else
|
||||||
log_info "포트 8080: 사용 안함"
|
log_info "포트 8888: 사용 안함"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
64
status.json
64
status.json
|
|
@ -6,7 +6,7 @@
|
||||||
"workers_by_status": {
|
"workers_by_status": {
|
||||||
"idle": [
|
"idle": [
|
||||||
{
|
{
|
||||||
"id": "worker_8aca7695",
|
"id": "worker_f42e10e1",
|
||||||
"status": "idle",
|
"status": "idle",
|
||||||
"task_count": 0,
|
"task_count": 0,
|
||||||
"error_count": 0,
|
"error_count": 0,
|
||||||
|
|
@ -37,62 +37,38 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"api_stats": {
|
"api_stats": {
|
||||||
"total_requests": 7,
|
"total_requests": 4,
|
||||||
"successful_requests": 6,
|
"successful_requests": 4,
|
||||||
"failed_requests": 1,
|
"failed_requests": 0,
|
||||||
"success_rate": 85.71428571428571,
|
"success_rate": 100.0,
|
||||||
"endpoint_usage": {
|
"endpoint_usage": {
|
||||||
"GET /health": 1,
|
"GET /health": 2,
|
||||||
"GET /": 1,
|
"GET /api/v1/model": 2
|
||||||
"GET /favicon.ico": 1,
|
|
||||||
"GET /docs": 2,
|
|
||||||
"GET /openapi.json": 2
|
|
||||||
},
|
},
|
||||||
"endpoint_stats": {
|
"endpoint_stats": {
|
||||||
"GET /health": {
|
"GET /health": {
|
||||||
"count": 1,
|
|
||||||
"avg_time": 0.0018818378448486328,
|
|
||||||
"min_time": 0.0018818378448486328,
|
|
||||||
"max_time": 0.0018818378448486328,
|
|
||||||
"current_concurrent": 0
|
|
||||||
},
|
|
||||||
"GET /": {
|
|
||||||
"count": 1,
|
|
||||||
"avg_time": 0.0030472278594970703,
|
|
||||||
"min_time": 0.0030472278594970703,
|
|
||||||
"max_time": 0.0030472278594970703,
|
|
||||||
"current_concurrent": 0
|
|
||||||
},
|
|
||||||
"GET /favicon.ico": {
|
|
||||||
"count": 1,
|
|
||||||
"avg_time": 0.002054452896118164,
|
|
||||||
"min_time": 0.002054452896118164,
|
|
||||||
"max_time": 0.002054452896118164,
|
|
||||||
"current_concurrent": 0
|
|
||||||
},
|
|
||||||
"GET /docs": {
|
|
||||||
"count": 2,
|
"count": 2,
|
||||||
"avg_time": 0.003388047218322754,
|
"avg_time": 0.001588582992553711,
|
||||||
"min_time": 0.0019888877868652344,
|
"min_time": 0.0013887882232666016,
|
||||||
"max_time": 0.0047872066497802734,
|
"max_time": 0.0017883777618408203,
|
||||||
"current_concurrent": 0
|
"current_concurrent": 0
|
||||||
},
|
},
|
||||||
"GET /openapi.json": {
|
"GET /api/v1/model": {
|
||||||
"count": 2,
|
"count": 2,
|
||||||
"avg_time": 0.02791738510131836,
|
"avg_time": 0.0014955997467041016,
|
||||||
"min_time": 0.002294301986694336,
|
"min_time": 0.0014739036560058594,
|
||||||
"max_time": 0.05354046821594238,
|
"max_time": 0.0015172958374023438,
|
||||||
"current_concurrent": 0
|
"current_concurrent": 0
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"average_response_time": 0.009942054748535156,
|
"average_response_time": 0.0015420913696289062,
|
||||||
"min_response_time": 0.0018818378448486328,
|
"min_response_time": 0.0013887882232666016,
|
||||||
"max_response_time": 0.05354046821594238,
|
"max_response_time": 0.0017883777618408203,
|
||||||
"current_concurrent": 0,
|
"current_concurrent": 0,
|
||||||
"max_concurrent": 1,
|
"max_concurrent": 1,
|
||||||
"requests_per_second": 0.004146781630742803,
|
"requests_per_second": 0.018707118250962246,
|
||||||
"uptime": 1688.056093454361,
|
"uptime": 213.82235074043274,
|
||||||
"recent_errors": []
|
"recent_errors": []
|
||||||
},
|
},
|
||||||
"timestamp": 1756394399.330041
|
"timestamp": 1756399332.7971911
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue