Nanbeige4.1-3B 서비스 운영 환경 배포: Supervisor 프로세스 관리 + logrotate 로그 로테이션 + 헬스 체크 구성

로컬 환경에서 Nanbeige4.1-3B 모델을 성공적으로 구동하여 강력한 추론 및 대화 능력을 확인했습니다. 하지만 이를 실제 서버 운영 환경에 배포하려고 할 때 다양한 문제에 직면할 수 있습니다.

  • 서비스가 갑자기 중단되어 수동으로 서버에 접속해 재시작해야 하는 상황
  • 로그 파일이 지속적으로 증가하여 디스크 공간을 가득 채워 서비스 장애를 유발하는 경우
  • 야간에 서비스 불가 알람이 발생했으나, 모델 로딩 실패 또는 WebUI 프로세스 종료 등 정확한 원인 파악이 어려운 상황

이러한 문제들은 '테스트 수준' 배포와 '운영 수준' 배포의 핵심적인 차이점입니다. 운영 환경에서는 서비스의 안정성, 신뢰성, 유지보수성이 필수적입니다. 본 문서에서는 Nanbeige4.1-3B를 위한 엔터프라이즈급 배포 솔루션을 단계별로 구축하는 방법을 설명합니다.

다음 세 가지 핵심 도구를 활용할 것입니다.

  1. Supervisor: WebUI 서비스가 충돌 후 자동으로 재시작되고, 시스템 부팅 시 자동으로 시작되도록 관리하는 프로세스 관리 도구입니다.
  2. logrotate: 로그 파일을 자동으로 분할, 압축, 정리하여 디스크 공간 부족을 방지하는 로그 관리 도구입니다.
  3. 헬스 체크 스크립트: 서비스 상태를 간편하게 확인할 수 있는 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 서비스는 자동 복구, 로그 관리, 상태 모니터링이 가능한 운영 가능한 시스템이 되었습니다.

태그: Supervisor logrotate Nanbeige4.1-3B WebUI 헬스체크

5월 27일 22:04에 게시됨