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 Applications → Terminal 실행 (또는 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.sh의APT_PKGS에openssh-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 naizvl→journalctl -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 컴파일. journalctl 에 Application 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* |
일정 시간 가동 후 모든 /infer 가 500 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 참고.