콘텐츠로 이동

NaizVL 오프라인 키트 — 설치 가이드

폐쇄망 운영 서버에 NaizVL VLM 추론 서버를 처음부터 설치하는 절차. 외부망 연결 없이 USB 키트만으로 진행.

이 문서는 키트가 이미 준비된 상태를 전제로 합니다. 키트 제작은 BUILD.md 참고.

0. 사전 준비 (반입 전 확인)

항목 확인
키트 USB naizvl-offline-kit-<버전>.tar.gz + .sha256 (+ 선택 .asc) 포함
Ubuntu 설치 USB Ubuntu Desktop 22.04.5 LTS (별도 USB, ISO ~4.7GB → USB ≥ 8GB)
운영 서버 RTX A6000 × 2, NVMe ≥ 256GB, RAM ≥ 32GB
관리자 정보 콘솔 접근 권한, 정적 IP, 게이트웨이, DNS, NTP
(선택) GPG 공개키 운영팀 사전 배포본 — 서명 검증 시 필요

권장: 키트 USB 와 Ubuntu 설치 USB 는 물리적으로 분리된 두 개로 준비 (포맷 사고 방지).


1. Ubuntu Desktop 22.04.5 LTS 설치 (USB 부팅)

1.1 Ubuntu 설치 USB 만들기 (반입 전, 별도 PC 에서)

  • ISO: ubuntu.com/download/desktop 에서 22.04.5 LTS 받기 (ubuntu-22.04.5-desktop-amd64.iso, ~4.7GB) — USB 8GB 이상 필요
  • USB 작성:
  • Linux: sudo dd if=ubuntu-22.04.5-desktop-amd64.iso of=/dev/sdX bs=4M status=progress conv=fsync (장치명 주의)
  • Windows: balenaEtcher 또는 Rufus (DD 모드)
  • 반드시 22.04.5 사용 — 키트의 kernel 6.8 / gcc-12 호환성이 이 마이너 버전에 맞춰져 있음.

1.2 BIOS / UEFI 부팅 순서 변경

서버 부팅 시 (보통 F2 / F10 / Del) 펌웨어 진입: - Secure Boot : Disabled 권장 (NVIDIA 드라이버 dkms 모듈 서명 회피) - Boot Order : USB 장치를 SSD 보다 위로 - 설정 저장 후 USB 꽂힌 상태로 재부팅

1.3 Ubuntu 설치 (Desktop 22.04.5 그래픽 설치)

USB 부팅 후 GRUB 메뉴에서 Try or Install Ubuntu 선택 → 라이브 데스크톱에서 Install Ubuntu 더블클릭.

단계 입력값
Welcome (언어) Korean 또는 English
Keyboard layout Korean / English
Wireless Don't connect to internet (폐쇄망 — 외부망 차단)
Updates and other software Normal installation 선택.
Download updates while installing 체크 해제.
Install third-party software for graphics … 체크 해제 (키트의 NVIDIA 드라이버와 충돌 방지)
Installation type Erase disk and install Ubuntu (전체 디스크 사용).
Advanced features → LVM / 암호화 는 운영 정책에 따름. 단순 ext4 권장.
Where are you Seoul
Who are you 이름 / hostname / 관리자 로그인 ID / 비밀번호. Log in automatically 체크 해제 권장 (보안).

설치 진행 (~10분) → Restart Now 클릭 → 화면 안내에 따라 USB 빼고 Enter.

1.4 첫 부팅 후 GUI 초기 설정

GNOME 첫 실행 환경설정 화면이 뜸: - Connect Your Online Accounts: Skip - Ubuntu Pro: Skip (Enable later) - Help improve Ubuntu: No, don't send system info - Privacy: Location Services Off - Ready to go: Done

첫 부팅 후 우상단 알림에 "Software Updater" 가 뜨면 무시/닫기. 외부망 차단 + 키트가 특정 패키지 버전에 맞춰져 있음.

1.5 터미널 + 네트워크 + 버전 확인

화면 좌하단 Show ApplicationsTerminal 실행 (또는 Ctrl+Alt+T):

# OS 버전 확인
lsb_release -a
# Distributor ID: Ubuntu
# Description:    Ubuntu 22.04.5 LTS
# Release:        22.04
# Codename:       jammy

uname -r
# 예: 6.8.0-40-generic   ← gcc-12 빌드 kernel. 키트가 자동 처리.

# 디스크 여유 (최소 50GB 권장)
df -h /

정적 IP 설정 (운영 네트워크 정책에 따라): - GUI: 우상단 네트워크 아이콘 → Wired Settings → 톱니바퀴 → IPv4 → Manual → Address/Netmask/Gateway/DNS 입력 - 또는 CLI: sudo nmtui (텍스트 UI) — Activate a connection → Edit → IPv4 → Manual

(선택) SSH 원격 접속 필요 시 — Desktop ISO 는 openssh-server 미설치. 현 키트 APT_PKGS 에 명시 안 됨. 두 옵션:

  • (권장) 키트 재제작 시 포함: 빌드 호스트에서 make-offline-kit.shAPT_PKGSopenssh-server 추가 후 키트 재빌드.
  • 현장 임시 처리: 빌드 호스트에서 apt-get download openssh-server openssh-sftp-server openssh-client libwrap0 받아 USB 로 운반 → sudo dpkg -i *.deb && sudo systemctl enable --now ssh

운영 콘솔에서 직접 install.sh 실행하면 SSH 없이도 됩니다 (sudo 비밀번호 + 재부팅 질문만 응답).

Ubuntu 설치 후 절대 apt update / apt upgrade 하지 말 것. 외부망 차단 + 키트가 특정 패키지 버전에 맞춰져 있음.


2. 키트 USB 이송 + 압축 해제

2.1 키트 USB 마운트

USB 꽂으면 GNOME 이 자동으로 /media/<사용자>/<라벨>/ 에 마운트합니다. 파일 매니저 (Files) 에서 USB 클릭 시 경로 확인 가능. 자동 마운트가 안 되거나 CLI 로 진행하려면:

# USB 장치 확인 (보통 /dev/sdb1)
lsblk
# NAME   MAJ:MIN RM   SIZE RO TYPE MOUNTPOINTS
# sda      8:0    0 465.8G  0 disk
# └─sda1   8:1    0 465.8G  0 part /
# sdb      8:16   1  58.6G  0 disk
# └─sdb1   8:17   1  58.6G  0 part

# 수동 마운트 (자동 마운트가 안 될 때)
sudo mkdir -p /mnt/usb
sudo mount /dev/sdb1 /mnt/usb

# 키트 파일 확인 (경로는 자동/수동 마운트 위치에 따라 조정)
ls -lh /media/$USER/*/naizvl-offline-kit-*.tar.gz* 2>/dev/null \
 || ls -lh /mnt/usb/naizvl-offline-kit-*.tar.gz*
# -rw-r--r-- 1 root root  25G  naizvl-offline-kit-v20260519-qwen3vl8b.tar.gz
# -rw-r--r-- 1 root root  102  naizvl-offline-kit-v20260519-qwen3vl8b.tar.gz.sha256

아래 명령 예시는 /mnt/usb 기준. GUI 자동 마운트 경로면 /media/$USER/<라벨> 로 바꿔 사용하세요.

2.2 체크섬 검증 (반드시 수행)

cd /mnt/usb
sha256sum -c naizvl-offline-kit-*.tar.gz.sha256
# naizvl-offline-kit-v20260519-qwen3vl8b.tar.gz: OK

실패 시 USB 전송 중 손상 → 다시 받아 재이송. OK 가 나올 때까지 진행 금지.

2.3 tar.gz 압축 해제

압축 해제 위치는 /tmp 또는 홈 디렉토리. /opt 직접 풀지 말 것 (install.sh 가 별도로 배치).

# 작업 디렉토리 (충분한 공간 — tar 25GB + 압축해제 ~30GB)
cd /tmp

# 압축 해제 (10~30분, USB 속도에 따라)
sudo tar xzf /mnt/usb/naizvl-offline-kit-v20260519-qwen3vl8b.tar.gz

# 해제된 디렉토리로 이동
cd naizvl-offline-kit-v20260519-qwen3vl8b

# 내부 구조 확인
ls
# 00-system  01-python  02-app  03-models
# install.sh  VERSION  BUILD_DATE
# MANIFEST.sha256  MANIFEST.sha256.asc

2.4 (선택) 내부 체크섬 + GPG 서명 검증

# 내부 체크섬 — 모든 파일 무결성
sha256sum -c MANIFEST.sha256 --quiet
# (출력 없으면 모두 OK)

# GPG 서명 — 운영팀 공개키 사전 배포 시
gpg --import <운영팀-공개키>.asc
gpg --verify MANIFEST.sha256.asc MANIFEST.sha256
# gpg: Good signature from "..."

install.sh 가 이 검증을 자동으로 한 번 더 수행하지만, 미리 확인하면 install 단계 진입 전 문제 차단 가능.


3. install.sh 실행

3.1 (선택) Dry-run — 패키지 영향 분석

sudo ./install.sh --dry-run
# .deb 별 SKIP / NEW / UPGRADE 표시. 실제 변경 없음.

3.2 정식 설치

sudo ./install.sh

수행 단계 (총 10단계, 진행 로그 콘솔 출력):

# 단계 내용
1 체크섬·서명 검증 MANIFEST.sha256 + (있으면) GPG 서명
2 서비스 계정 naiz 계정 생성/확인
3 기본 시스템 .deb dependencies/ 를 local apt repo 로 등록 후 apt-get install
4 NVIDIA 드라이버 + CUDA 12.8 gcc-12 alternative 자동 설정 후 dkms compile
5 애플리케이션 배치 /opt/naizvl/ 에 3개 파일 + 로그 디렉토리
6 Python wheel 설치 시스템 python3.10 에 system-wide. 4 sub-step (6-1~6-4): user-local 정리 → --force-reinstall → NCCL ldconfig → PYTHONNOUSERSITE=1 검증
7 모델 복사 03-models//opt/naizvl/Qwen3-VL-8B-Instruct/ (~16GB)
8 config.yaml 초기화 config.example.yaml 기반
9 systemd unit 등록 + watchdog 배포 naizvl.service 활성화. naizvl-watchdog.sh/usr/local/bin/ + state 디렉토리 /var/lib/naizvl/. cron 등록은 수동 안내 (§11).
10 재부팅 질문 NVIDIA 드라이버 신규 설치 시

소요 시간: ~10~25분 (NVMe 속도 + 모델 크기 의존).

3.3 재부팅

NVIDIA 드라이버 신규 설치 시 install.sh 가 질문 → y 응답.

sudo reboot

수동 재부팅이면:

sudo reboot


4. 재부팅 후 검증

4.1 GPU 인식 확인

nvidia-smi --query-gpu=index,name,driver_version,memory.total --format=csv
# index, name, driver_version, memory.total [MiB]
# 0, NVIDIA RTX A6000, 570.86.10, 48685 MiB
# 1, NVIDIA RTX A6000, 570.86.10, 48685 MiB

2장 모두 인식되어야 정상. 1장만 보이면 §7 문제 해결 참고.

4.2 서비스 기동

sudo systemctl start naizvl
sudo systemctl status naizvl
sudo journalctl -u naizvl -f      # Ctrl-C 로 중단

첫 부팅은 ~1분 (모델 로드 + vLLM KV cache 초기화 + Triton JIT 컴파일). Application startup complete. 가 나오면 ready.

4.3 응답 확인

curl -sS http://127.0.0.1:7860/status
# {"status":"running"}

5. 최초 admin 키 확인

첫 기동 시 _sanitize_api_keys() 가 admin 키를 자동 생성하여 /opt/naizvl/.admin_bootstrap 에 기록.

sudo cat /opt/naizvl/.admin_bootstrap
# {"key":"..."}

5.1 정식 admin 키 발급

ADMIN=$(sudo cat /opt/naizvl/.admin_bootstrap | jq -r .key)

curl -X POST http://localhost:7860/sessions/create \
     -H "X-API-Key: $ADMIN" \
     -H "Content-Type: application/json" \
     -d '{"comment":"현장 admin #1", "role":"admin"}'
# {"key":"NEW-ADMIN-KEY-..."}

5.2 bootstrap 키 삭제 (보안)

NEW_ADMIN=<위에서 받은  admin 키>

curl -X POST http://localhost:7860/sessions/delete \
     -H "X-API-Key: $NEW_ADMIN" \
     -H "Content-Type: application/json" \
     -d "{\"key\":\"$ADMIN\"}"

sudo rm /opt/naizvl/.admin_bootstrap

새 admin 키는 별도 안전 기록장에 보관. 분실 시 재발급 불가 (서비스 재초기화 필요).


6. 현장별 설정

항목 파일 조치
trusted_ips /opt/naizvl/config.yaml 관리자 PC / 내부망 대역 추가 (예: 192.168.100.0/24)
port 동상 기본 7860 외 필요 시 변경
추가 user 키 /sessions/create API 운영 사용자에게 발급

설정 변경 후:

sudo systemctl restart naizvl


7. 서버 2대 설치 체크리스트

각 서버마다 반복:

  • [ ] §1 Ubuntu Desktop 22.04.5 LTS 설치 (USB) + 정적 IP + (선택) SSH
  • [ ] df -h //opt 여유 50GB 이상 확인
  • [ ] §2 키트 USB 마운트 → sha256sum -c 검증 → tar xzf
  • [ ] (선택) MANIFEST.sha256 + GPG 서명 검증
  • [ ] §3 sudo ./install.sh --dry-run (선택) → sudo ./install.sh
  • [ ] 재부팅
  • [ ] §4 nvidia-smi — GPU 2장 인식
  • [ ] sudo systemctl start naizvljournalctl -u naizvl -f 로 모델 로드 확인 (~1분)
  • [ ] curl http://localhost:7860/status{"status":"running"}
  • [ ] §5 /opt/naizvl/.admin_bootstrap 키 → 새 admin 키 발급 → bootstrap 삭제 → 안전 기록
  • [ ] §6 config.yaml.trusted_ips 편집 → systemctl restart naizvl

서버 #1·#2 주의: - admin 키는 서버마다 독립. 같은 키를 양쪽에 넣지 말 것. - trusted_ips 는 동일하게 설정 가능 (운영 PC 공통).


8. 문제 해결

증상 원인 · 조치
install.sh: Permission denied sudo 누락. sudo ./install.sh
sha256sum -c FAIL USB 전송 손상. 재이송.
gpg: BAD signature 공개키 잘못됨 또는 위·변조 의심. 운영팀 확인.
nvidia-smi 실패 재부팅 누락. sudo reboot 후 재확인.
GPU 1장만 인식 (a) 두번째 GPU 전원/PCIe 연결 점검. (b) lspci -nn \| grep -i nvidia 로 하드웨어 확인 후 드라이버 재설치
dkms 컴파일 실패: cc: error: unrecognized command-line option '-ftrivial-auto-var-init=zero' kernel 6.8 이 gcc-12 로 빌드되어 dkms 가 gcc-12 요구. install.sh 가 자동 등록하지만 실패 시 수동: sudo update-alternatives --set cc /usr/bin/gcc-12 && sudo dpkg-reconfigure nvidia-dkms-570-open
vLLM 첫 inference 직전 fatal error: Python.h: No such file or directory 키트에 Python C 헤더 누락 (구 키트). 4 .deb hotfix (libexpat1-dev, zlib1g-dev, libpython3.10-dev, python3.10-dev) sudo dpkg -i. 신규 키트는 자동 포함.
기동 시 ImportError: vllm /usr/bin/python3.10 -c "import vllm; print(vllm.__version__)" 확인. 실패 시 pip3 list \| grep vllm
Repository Not Found for url HF 오프라인 플래그 확인 (systemctl show naizvl \| grep OFFLINE) — HF_HUB_OFFLINE=1 필요
첫 응답 지연 (~1분) 정상. vLLM KV cache 초기화 + Triton JIT 컴파일. journalctlApplication startup complete. 확인 후 가용.
Port 7860 접속 불가 ufw / iptables 방화벽 규칙 확인. sudo ss -tlnp \| grep 7860
SystemError: pydantic-core 버전 불일치 user-local 의 구버전 우선됨. install.sh 단계 6-1 이 자동 제거. 수동: rm -rf /home/naiz/.local/lib/python3.10/site-packages/pydantic*
cannot import name 'is_offline_mode' from 'huggingface_hub' 동일 — user-local 의 구 huggingface_hub. install.sh 6-1 미실행 시 수동 제거.
libtorch_cuda.so: undefined symbol: ncclCommShrink apt 의 구 NCCL 이 우선 로드. ldconfig -p \| grep nccl 에서 /usr/local/.../nvidia/nccl/lib/libnccl.so.2 가 첫 줄에 있어야 정상. 없으면 install.sh 6-3 재실행. 수동: echo "/usr/local/lib/python3.10/dist-packages/nvidia/nccl/lib" \| sudo tee /etc/ld.so.conf.d/zz-nvidia-nccl-cu12.conf && sudo ldconfig
flash_attn_2_cuda...so: undefined symbol: _ZN3c104cuda29c10_cuda_check... flash-attn 이 다른 torch 로 빌드됨. 키트의 wheel 은 빌드 측 torch 와 일치. --force-reinstall 로 자동 교체. 수동: pip uninstall -y flash-attn && pip install --no-index --find-links=01-python/wheels --break-system-packages flash-attn
ModuleNotFoundError: scipy (PYTHONNOUSERSITE=1) 구 키트에 scipy 누락. 현 키트는 REQUIRED_EXTRAS 자동 포함. 수동: sudo pip3 install --no-index --find-links=01-python/wheels --break-system-packages scipy scikit-learn
Engine core initialization failed + flash_attn ImportError flash-attn wheel 없거나 ABI 불일치. (a) 키트 wheel 사용 또는 (b) 시스템 flash_attn 제거 후 vllm fallback: sudo rm -rf /usr/local/lib/python3.10/dist-packages/flash_attn*
일정 시간 가동 후 모든 /infer500 Internal Server Error (journal 에 traceback 없음) Ray Serve replica unhealthy. 즉시 복구: sudo systemctl restart naizvl. 재발 방지: §11 watchdog cron 활성화 — 6분 내 자동 재시작. 코드 fix 로 다음번엔 journal 에 traceback 자동 출력 (HARDENING.md 참고)
naizvl-watchdog.sh: curl: command not found Ubuntu Desktop minimal install 에 curl 빠짐. 새 watchdog (2026-05-26+) 는 curl → wget → python3 자동 fallback. 구버전이면: 새 키트의 curl*.deb 설치 또는 watchdog 교체. python3 fallback 만으로도 동작은 OK.

9. 운영 환경 정책 요약

systemd unit 환경변수가 강제하는 핵심 정책:

  • PYTHONNOUSERSITE=1 — user-local site-packages 무시 (시스템 dist-packages 만)
  • LD_LIBRARY_PATH=/usr/local/lib/python3.10/dist-packages/nvidia/nccl/lib — torch 동봉 NCCL 우선
  • HF_HUB_OFFLINE=1 / TRANSFORMERS_OFFLINE=1 — 폐쇄망 필수

검증:

PYTHONNOUSERSITE=1 /usr/bin/python3.10 -c \
    "import vllm, transformers, fastapi, ruamel.yaml; print('vllm', vllm.__version__)"

10. 관련 문서

  • BUILD.md — 키트 제작 가이드
  • INSTALL_FRONTEND.md — 웹 UI 설치
  • HARDENING.md — 운영 안정화 (인시던트 대응 + watchdog)
  • README.md — 변경 이력 + 인덱스
  • ../research.md §20 (NaizVL 저장소 참고) — 서버 전체 구조 스냅샷

11. Watchdog 활성화 (운영 권장)

install.sh/usr/local/bin/naizvl-watchdog.sh/var/lib/naizvl/ 까지는 자동 배포. cron 등록은 수동 (root crontab 멱등성 어려워 자동화 안 함).

11.1 cron 등록

sudo crontab -e

다음 줄 추가:

*/2 * * * * /usr/local/bin/naizvl-watchdog.sh

→ 2분마다 /healthz 점검, 연속 3회 실패 시 systemctl restart naizvl 자동 실행.

11.2 동작 확인

# 수동 실행 — 정상이면 출력 없음
sudo /usr/local/bin/naizvl-watchdog.sh

# 로그 (cron 실행 후 5~10분 뒤 확인)
journalctl -t naizvl-watchdog --since "10 min ago"
# 정상이면 fail/restart 라인 없음. 카운터 reset 메시지만 종종.

11.3 파라미터 튜닝 (환경변수)

변수 default 의미
NAIZVL_WATCHDOG_FAIL 3 재시작 트리거 연속 실패 횟수
NAIZVL_WATCHDOG_TIMEOUT 5 /healthz 응답 timeout (초)
NAIZVL_WATCHDOG_STATE /var/lib/naizvl/watchdog.state 카운터 파일 경로
NAIZVL_SERVER http://127.0.0.1:7860 점검 대상 URL

cron 라인에 환경변수 전달:

*/2 * * * * NAIZVL_WATCHDOG_FAIL=5 /usr/local/bin/naizvl-watchdog.sh

11.4 비활성화

sudo crontab -e
# */2 * * * * /usr/local/bin/naizvl-watchdog.sh 줄 삭제 또는 # 주석

11.5 점검 명령

# 현재 fail 카운터 (0 = 모두 정상)
sudo cat /var/lib/naizvl/watchdog.state

# 직접 /healthz 호출 (인증 불필요)
curl -sS http://127.0.0.1:7860/healthz
# {"ok":true,"active":0,"completed":N}

# 재시작 이력 (watchdog 트리거 또는 수동)
journalctl -u naizvl | grep -i "started\|starting"

상세 운영·튜닝 가이드는 HARDENING.md 참고.