로컬 환경에서 Nanbeige4.1-3B 모델을 성공적으로 구동하여 강력한 추론 및 대화 능력을 확인했습니다. 하지만 이를 실제 서버 운영 환경에 배포하려고 할 때 다양한 문제에 직면할 수 있습니다.
- 서비스가 갑자기 중단되어 수동으로 서버에 접속해 재시작해야 하는 상황
- 로그 파일이 지속적으로 증가하여 디스크 공간을 가득 채워 서비스 장애를 유발하는 경우
- 야간에 서비스 불가 알람이 발생했으나, 모델 로딩 실패 또는 WebUI 프로세스 종료 등 정확한 원인 파악이 어려운 상황
이러한 문제들은 '테스트 수준' 배포와 '운영 수준' 배포의 핵심적인 차이점입니다. 운영 환경에서는 서비스의 안정성, 신뢰성, 유지보수성이 필수적입니다. 본 문서에서는 Nanbeige4.1-3B를 위한 엔터프라이즈급 배포 솔루션을 단계별로 구축하는 방법을 설명합니다.
다음 세 가지 핵심 도구를 활용할 것입니다.
- Supervisor: WebUI 서비스가 충돌 후 자동으로 재시작되고, 시스템 부팅 시 자동으로 시작되도록 관리하는 프로세스 관리 도구입니다.
- logrotate: 로그 파일을 자동으로 분할, 압축, 정리하여 디스크 공간 부족을 방지하는 로그 관리 도구입니다.
- 헬스 체크 스크립트: 서비스 상태를 간편하게 확인할 수 있는 HTTP 엔드포인트 또는 스크립트를 제공하여 모니터링 시스템과의 통합을 용이하게 합니다.
배포 환경 및 프로젝트 준비
환경 확인
SSH를 통해 서버에 접속하여 필수 구성 요소가 설치되어 있는지 확인합니다.
# Python 버전 확인
python3 --version
# CUDA 및 PyTorch 가용성 확인 (GPU 사용 시)
python3 -c "import torch; print(f'PyTorch 버전: {torch.__version__}'); print(f'CUDA 사용 가능: {torch.cuda.is_available()}')"
# 모델 파일 존재 여부 확인
ls -lh /root/ai-models/nanbeige/Nanbeige4___1-3B/ | head -5
프로젝트 디렉토리 구조
WebUI 코드가 /root/nanbeige-webui에 위치한다고 가정합니다.
/root/nanbeige-webui/
├── webui.py # Gradio WebUI 메인 프로그램
├── start.sh # 시작 스크립트
├── stop.sh # 중지 스크립트 (선택 사항)
├── health_check.py # 헬스 체크 스크립트
├── requirements.txt # Python 의존성 패키지 목록
├── logs/ # 애플리케이션 로그 디렉토리
│ └── .gitkeep
└── README.md
신뢰할 수 있는 시작 스크립트 생성
/root/nanbeige-webui/start.sh 파일을 생성하거나 수정합니다.
#!/bin/bash
# Nanbeige4.1-3B WebUI 운영 환경 시작 스크립트
set -e
cd "$(dirname "$0")"
# 환경 변수 설정
export PYTHONPATH=/root/nanbeige-webui:$PYTHONPATH
# Python 가상 환경 활성화 (사용하는 경우)
# source /path/to/your/venv/bin/activate
# conda activate nanbeige
echo "$(date '+%Y-%m-%d %H:%M:%S') - Nanbeige WebUI 서비스 시작 중..."
# Gradio 애플리케이션 실행
exec python3 webui.py \
--server-name 0.0.0.0 \
--server-port 7860 \
--log-level info
실행 권한을 부여합니다.
chmod +x /root/nanbeige-webui/start.sh
Supervisor를 사용한 프로세스 관리
Supervisor 설치
sudo apt update
sudo apt install supervisor -y
설치 후 상태를 확인합니다.
sudo systemctl status supervisor
Nanbeige 서비스 설정 파일 생성
/etc/supervisor/conf.d/nanbeige-webui.conf 파일을 생성하고 다음과 같이 설정합니다.
; Nanbeige4.1-3B WebUI 프로세스 설정
[program:nanbeige-webui]
command=/root/nanbeige-webui/start.sh
directory=/root/nanbeige-webui
user=root
autostart=true
autorestart=true
startretries=3
stopsignal=TERM
stopwaitsecs=30
stdout_logfile=/var/log/supervisor/nanbeige-webui-stdout.log
stderr_logfile=/var/log/supervisor/nanbeige-webui-stderr.log
stdout_logfile_maxbytes=50MB
stderr_logfile_maxbytes=50MB
stdout_logfile_backups=5
stderr_logfile_backups=5
; 환경 변수
environment=PYTHONPATH="/root/nanbeige-webui",PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
설정 활성화 및 서비스 관리
# Supervisor 설정 다시 로드
sudo supervisorctl reread
sudo supervisorctl update
# 서비스 시작
sudo supervisorctl start nanbeige-webui
# 상태 확인
sudo supervisorctl status nanbeige-webui
# 중지
sudo supervisorctl stop nanbeige-webui
# 재시작
sudo supervisorctl restart nanbeige-webui
logrotate를 사용한 로그 로테이션
logrotate 설정 파일 생성
/etc/logrotate.d/supervisor-nanbeige 파일을 생성합니다.
/var/log/supervisor/nanbeige-webui-*.log {
daily
missingok
rotate 30
compress
delaycompress
notifempty
create 0640 root adm
sharedscripts
postrotate
/usr/bin/supervisorctl signal HUP nanbeige-webui 2>/dev/null || true
endscript
}
설정 테스트
# 설정 검증
sudo logrotate -d /etc/logrotate.d/supervisor-nanbeige
# 강제 실행 테스트
sudo logrotate -vf /etc/logrotate.d/supervisor-nanbeige
헬스 체크 구현
헬스 체크 스크립트 생성
/root/nanbeige-webui/health_check.py 파일을 생성합니다.
#!/usr/bin/env python3
import sys
import json
import torch
from http.server import HTTPServer, BaseHTTPRequestHandler
from transformers import AutoModelForCausalLM, AutoTokenizer
import threading
model_instance = None
tokenizer_instance = None
model_loaded_flag = False
load_lock = threading.Lock()
def load_model():
global model_instance, tokenizer_instance, model_loaded_flag
if model_loaded_flag:
return True
with load_lock:
if model_loaded_flag:
return True
try:
model_path = "/root/ai-models/nanbeige/Nanbeige4___1-3B"
print(f"헬스 체크 서비스: 모델 로딩 중 {model_path}...")
tokenizer_instance = AutoTokenizer.from_pretrained(
model_path,
trust_remote_code=True
)
model_instance = AutoModelForCausalLM.from_pretrained(
model_path,
torch_dtype=torch.bfloat16,
device_map="auto",
trust_remote_code=True
)
model_loaded_flag = True
print("헬스 체크 서비스: 모델 로딩 성공.")
return True
except Exception as e:
print(f"헬스 체크 서비스: 모델 로딩 실패 - {e}")
return False
def check_health():
if not load_model():
return {"status": "error", "message": "모델 로딩 실패", "code": 500}
try:
test_messages = [{"role": "user", "content": "'헬스 체크 정상'이라고 응답하세요."}]
input_ids = tokenizer_instance.apply_chat_template(
test_messages,
return_tensors="pt"
).to(model_instance.device)
with torch.no_grad():
outputs = model_instance.generate(
input_ids,
max_new_tokens=10,
do_sample=False
)
response = tokenizer_instance.decode(
outputs[0][len(input_ids[0]):],
skip_special_tokens=True
)
if "헬스 체크 정상" in response:
return {
"status": "healthy",
"message": "서비스 정상 작동",
"model_response": response,
"code": 200
}
else:
return {
"status": "degraded",
"message": "서비스 응답 이상",
"model_response": response,
"code": 206
}
except Exception as e:
return {"status": "error", "message": f"추론 과정 오류: {str(e)}", "code": 503}
class HealthHandler(BaseHTTPRequestHandler):
def do_GET(self):
if self.path == '/health':
health_info = check_health()
self.send_response(health_info['code'])
self.send_header('Content-type', 'application/json')
self.end_headers()
response = json.dumps(health_info, ensure_ascii=False)
self.wfile.write(response.encode('utf-8'))
else:
self.send_response(404)
self.end_headers()
self.wfile.write(b'Not Found')
def log_message(self, format, *args):
pass
def run_server(port=8080):
server_address = ('0.0.0.0', port)
httpd = HTTPServer(server_address, HealthHandler)
print(f'헬스 체크 서비스 시작됨, 포트 {port}, 경로 /health')
httpd.serve_forever()
if __name__ == '__main__':
preload_thread = threading.Thread(target=load_model, daemon=True)
preload_thread.start()
run_server(port=8080)
Supervisor로 헬스 체크 서비스 관리
/etc/supervisor/conf.d/nanbeige-health.conf 파일을 생성합니다.
[program:nanbeige-health]
command=python3 /root/nanbeige-webui/health_check.py
directory=/root/nanbeige-webui
user=root
autostart=true
autorestart=true
startretries=3
stopsignal=TERM
stopwaitsecs=10
stdout_logfile=/var/log/supervisor/nanbeige-health-stdout.log
stderr_logfile=/var/log/supervisor/nanbeige-health-stderr.log
stdout_logfile_maxbytes=10MB
stderr_logfile_maxbytes=10MB
stdout_logfile_backups=3
stderr_logfile_backups=3
environment=PYTHONPATH="/root/nanbeige-webui",PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl start nanbeige-health
sudo supervisorctl status nanbeige-health
헬스 체크 엔드포인트 테스트
curl http://localhost:8080/health
정상 응답 예시:
{
"status": "healthy",
"message": "서비스 정상 작동",
"model_response": "헬스 체크 정상.",
"code": 200
}
운영 환경 고급 제안
- 비root 사용자 실행: 서비스 전용 시스템 사용자를 생성하여 보안을 강화합니다.
- 리소스 제한: Supervisor 설정에서
OMP_NUM_THREADS,CUDA_VISIBLE_DEVICES등의 환경 변수를 설정합니다. - 모니터링 고도화: Prometheus + Grafana를 도입하여 시각적 모니터링 및 알림 체계를 구축합니다.
- 리버스 프록시: Nginx를 앞단에 배치하여 HTTPS, 로드 밸런싱, 접근 제어를 구현합니다.
이제 Nanbeige4.1-3B 서비스는 자동 복구, 로그 관리, 상태 모니터링이 가능한 운영 가능한 시스템이 되었습니다.