본문 바로가기
DEV & DevOps/Backend

폐쇄망 Ollama→vLLM 전환기 EP.2 — 컨테이너 이미지 & 모델 반입

by Hoft 2026. 3. 24.

SERIES

🔧 폐쇄망 Ollama→vLLM 전환기

RHEL + V100 × 3 + Podman 환경에서 Qwen3-Coder-30B를 vLLM으로 서빙하기까지의 여정

📋 시리즈 목차

✔ EP.1 서버 환경 사전 점검
▸ EP.2 컨테이너 이미지 & 모델 반입 (현재 글)
▸ EP.3 vLLM 실행 및 트러블슈팅
▸ EP.4+ 성능 튜닝 & 운영 (계속 업데이트)

EP.1에서 서버 환경 점검을 마쳤습니다. 이제 실제로 vLLM 컨테이너 이미지와 모델 파일을 폐쇄망 서버로 옮기는 단계입니다.
인터넷이 되는 외부 PC에서 다운로드 → 파일 반입 → 서버에서 로드. 이 세 단계를 하나씩 짚어봅니다.

🔹 전체 흐름 한눈에 보기

① 외부 PC — vLLM 이미지 pull → save, 모델 다운로드
② 전송 — USB / 망간자료전송 / SCP로 반입
③ 서버 — podman load, 모델 파일 배치, 체크섬 검증

⚠️ 총 필요 용량: 컨테이너 이미지 ~10GB + 모델 파일 ~61GB (FP16 기준) = 약 70GB 이상. 반입 수단의 용량 제한을 반드시 먼저 확인하세요.

 


🔹 뭘 다운받아야 하나?

항목 크기 출처 비고
vLLM 컨테이너 이미지 ~10GB Docker Hub CUDA, Python, NCCL 모두 포함
Qwen3-Coder-30B-A3B (MoE, 활성 3B) ~61GB HuggingFace 코딩 특화, 추론 시 3B만 활성
Qwen3-32B (Dense) ~65GB HuggingFace 범용 모델, 최대 품질, GPU 3장 필요

💡 어떤 모델을 골라야 할까?
코딩 용도라면 Qwen3-Coder-30B-A3B-Instruct가 최선입니다. MoE 구조라 추론 시 3B만 활성화되어 속도가 빠릅니다.
범용 (채팅, 문서 작성 등)이라면 Qwen3-32B Dense 모델이 품질이 더 좋습니다. 단, GPU 3장 텐서 병렬이 필요합니다.
⚠️ 참고: Qwen3-Coder에는 Dense 30B 모델이 없습니다. MoE(30B-A3B)만 존재합니다.

🔹 vLLM 컨테이너 이미지 다운로드

인터넷이 되는 외부 PC에서 실행합니다. Docker 또는 Podman이 설치되어 있어야 합니다.

# 1. vLLM 공식 이미지 pull
# ⚠️ 외부 PC가 ARM Mac이면 반드시 --platform 지정
podman pull --platform linux/amd64 docker.io/vllm/vllm-openai:latest

# Docker를 사용하는 경우
docker pull --platform linux/amd64 vllm/vllm-openai:latest

# 2. tar 파일로 저장
podman save -o vllm-openai-latest.tar docker.io/vllm/vllm-openai:latest

# Docker의 경우
docker save -o vllm-openai-latest.tar vllm/vllm-openai:latest

# 3. 파일 크기 및 체크섬 확인
ls -lh vllm-openai-latest.tar
sha256sum vllm-openai-latest.tar > vllm-image-checksum.txt
cat vllm-image-checksum.txt

🔥 latest 대신 특정 버전을 쓸까?
운영 안정성을 위해 vllm/vllm-openai:v0.8.4 처럼 버전을 고정하는 것도 좋습니다. 단, V100(Compute Capability 7.0) 호환을 위해 v0.6.0 이상을 권장합니다. 최신 버전일수록 성능이 좋지만, 폐쇄망에서는 이미지 교체가 어려우니 신중하게 선택하세요.

💡 Docker 이미지의 ENTRYPOINT: 공식 이미지는 ENTRYPOINT ["vllm", "serve"]로 설정되어 있어서, 이미지 뒤에 모델 경로와 옵션만 붙이면 바로 실행됩니다.

🔹 모델 파일 다운로드

HuggingFace에서 모델 파일을 다운로드합니다. 최신 huggingface_hub에서는 hf 명령어를 사용합니다.
(기존 huggingface-cli도 동작하지만 deprecated 경고가 표시됩니다.)

# 1. huggingface_hub 설치 (외부 PC에서)
pip install -U huggingface_hub

# 2-A. Qwen3-Coder MoE 모델 다운로드 (~61GB)
hf download Qwen/Qwen3-Coder-30B-A3B-Instruct \
  --local-dir ./Qwen3-Coder-30B-A3B-Instruct

# 2-B. 또는 Qwen3-32B Dense 범용 모델 (~65GB)
hf download Qwen/Qwen3-32B \
  --local-dir ./Qwen3-32B

# ⚠️ 구버전(huggingface_hub < 0.28)이면 기존 명령어 사용
# huggingface-cli download Qwen/Qwen3-Coder-30B-A3B-Instruct --local-dir ./Qwen3-Coder-30B-A3B-Instruct

다운로드 후 확인할 것

# 파일 목록 확인 — safetensors 파일이 있어야 함
ls -lh Qwen3-Coder-30B-A3B-Instruct/
# 예상 파일: model-00001-of-000XX.safetensors, config.json,
#           tokenizer.json, tokenizer_config.json 등

# 전체 크기 확인
du -sh Qwen3-Coder-30B-A3B-Instruct/

# 체크섬 생성 (폴더 단위)
find Qwen3-Coder-30B-A3B-Instruct/ -type f -exec sha256sum {} \; > model-checksums.txt

💡 특정 파일만 받기: 용량이 걱정되면 --include "*.safetensors" "*.json"으로 필수 파일만 받을 수 있습니다. .bin 파일은 safetensors가 있으면 불필요합니다.

💡 Podman 실행 시 --ipc=host 필수: vLLM은 텐서 병렬 시 PyTorch의 공유 메모리(shared memory)를 사용합니다. 이 옵션이 없으면 multi-GPU 모델이 IPC 에러로 실패합니다.

🔹 폐쇄망으로 반입하기

여기가 가장 번거로운 단계입니다. 반입 수단에 따라 파일 처리 방법이 달라집니다.

방법 A: USB 반입 (FAT32)

FAT32는 파일당 4GB 제한이 있으므로 분할이 필요합니다.

# ── 외부 PC에서 ──

# 컨테이너 이미지 분할 (3.9GB 단위)
split -b 3900m vllm-openai-latest.tar vllm-img-part-

# 모델 파일을 tar로 묶은 뒤 분할
tar cf qwen3-coder-30b.tar Qwen3-Coder-30B-A3B-Instruct/
split -b 3900m qwen3-coder-30b.tar qwen3-model-part-

# 분할 파일 목록 확인
ls -lh vllm-img-part-* qwen3-model-part-*

# 체크섬 파일도 함께 USB에 복사
cp vllm-image-checksum.txt model-checksums.txt [USB경로]/
# ── 서버에서 ──

# USB 마운트
sudo mount /dev/sdb1 /mnt/usb

# 작업 디렉토리로 복사
cp /mnt/usb/vllm-img-part-* /sw/aimon/vllm-setup/
cp /mnt/usb/qwen3-model-part-* /sw/aimon/vllm-setup/
cp /mnt/usb/*checksum* /sw/aimon/vllm-setup/

# 분할 파일 합치기
cd /sw/aimon/vllm-setup/
cat vllm-img-part-* > vllm-openai-latest.tar
cat qwen3-model-part-* > qwen3-coder-30b.tar

# 체크섬 검증
sha256sum -c vllm-image-checksum.txt

방법 B: USB 반입 (exFAT/NTFS)

파일 크기 제한이 없으므로 분할 불필요. 단, RHEL에서 exFAT 마운트 가능 여부를 미리 확인해야 합니다.

# exFAT 마운트 지원 확인
rpm -qa | grep -i exfat

# 지원 안 되면 → FAT32 분할 방법으로 전환
# 지원 되면 → 그냥 복사
sudo mount /dev/sdb1 /mnt/usb
cp /mnt/usb/vllm-openai-latest.tar /sw/aimon/vllm-setup/
cp -r /mnt/usb/Qwen3-Coder-30B-A3B-Instruct/ /rep/aimon/models/

방법 C: 망간자료전송 시스템

💡 기관마다 건당 용량 제한(보통 2~10GB)이 다릅니다. 제한을 확인하고 split으로 맞춰서 분할하세요.
전송 후 반드시 체크섬을 비교하여 파일 무결성을 확인합니다.

🔹 서버에서 이미지 로드 & 모델 배치

Step 1: 컨테이너 이미지 로드

# Podman에 이미지 로드
podman load -i /sw/aimon/vllm-setup/vllm-openai-latest.tar

# 로드 확인
podman images | grep vllm
# 출력 예: docker.io/vllm/vllm-openai  latest  abc123def  8.2GB

Step 2: 모델 파일 배치

# 모델 디렉토리 생성
mkdir -p /rep/aimon/models

# tar로 묶어왔다면 풀기
cd /rep/aimon/models
tar xf /sw/aimon/vllm-setup/qwen3-coder-30b.tar

# 또는 이미 폴더로 복사했다면 위치만 확인
ls /rep/aimon/models/Qwen3-Coder-30B-A3B-Instruct/
# config.json, tokenizer.json, model-*.safetensors 등이 있어야 함

Step 3: 체크섬 최종 검증

# 모델 파일 체크섬 검증
cd /rep/aimon/models
sha256sum -c /sw/aimon/vllm-setup/model-checksums.txt

# 모두 OK가 나와야 함
# 하나라도 FAILED가 나오면 해당 파일 재반입 필요

Step 4: 임시 파일 정리

# 분할 파일, tar 파일 등 정리 (디스크 여유 확보)
rm /sw/aimon/vllm-setup/vllm-img-part-*
rm /sw/aimon/vllm-setup/qwen3-model-part-*
rm /sw/aimon/vllm-setup/qwen3-coder-30b.tar
# vllm-openai-latest.tar는 롤백용으로 보관해도 좋음

# 디스크 여유 확인
df -h /var/lib/containers /rep/aimon /sw/aimon

🔹 반입 검증 체크리스트

다음 단계(EP.3 vLLM 실행)로 넘어가기 전에, 아래 항목을 모두 확인하세요.

확인 항목 확인 명령어
vLLM 이미지 로드 완료 podman images | grep vllm
모델 파일 존재 ls /rep/aimon/models/Qwen3-Coder-30B-A3B-Instruct/*.safetensors
config.json 존재 cat /rep/aimon/models/Qwen3-Coder-30B-A3B-Instruct/config.json | head -5
tokenizer 파일 존재 ls /rep/aimon/models/Qwen3-Coder-30B-A3B-Instruct/tokenizer*
체크섬 검증 통과 sha256sum -c model-checksums.txt
디스크 여유 충분 df -h (각 파티션 여유 확인)
GPU 사용 가능 nvidia-smi (GPU 3장 인식 확인)

💡 한방 검증 스크립트:

echo "── vLLM 이미지 ──"
podman images | grep vllm || echo "❌ 이미지 없음"
echo ""
echo "── 모델 파일 ──"
ls /rep/aimon/models/Qwen3-Coder-30B-A3B-Instruct/*.safetensors 2>/dev/null | wc -l | xargs -I{} echo "safetensors 파일: {}개"
ls /rep/aimon/models/Qwen3-Coder-30B-A3B-Instruct/config.json 2>/dev/null && echo "✅ config.json" || echo "❌ config.json 없음"
ls /rep/aimon/models/Qwen3-Coder-30B-A3B-Instruct/tokenizer.json 2>/dev/null && echo "✅ tokenizer.json" || echo "❌ tokenizer.json 없음"
echo ""
echo "── 디스크 ──"
df -h /var/lib/containers /rep/aimon
echo ""
echo "── GPU ──"
nvidia-smi --query-gpu=index,name,memory.free --format=csv,noheader

⚠️ 자주 발생하는 실수

ARM 이미지를 가져온 경우 — 외부 PC가 M1/M2 Mac이면 --platform linux/amd64 없이 pull하면 ARM 이미지가 받아짐. 서버에서 exec format error 발생.
모델 폴더 구조가 깨진 경우config.json이 최상위에 없고 하위 폴더에 있으면 vLLM이 모델을 인식 못함. ls로 구조를 꼭 확인.
safetensors 파일 누락 — 60GB 모델은 파일이 수십 개로 나뉘어 있음. 전송 중 하나라도 빠지면 로딩 실패. 체크섬 필수.
split 후 합칠 때 순서cat part-*는 알파벳 순으로 합침. split 기본 접미사(aa, ab, ac...)가 이를 보장하므로 파일명을 변경하지 마세요.

이제 서버에 vLLM 이미지와 모델 파일이 모두 준비되었습니다.
다음 글에서는 드디어 vLLM 컨테이너를 실행하고, 발생할 수 있는 에러들을 하나씩 해결하는 과정을 다룹니다.

폐쇄망 반입, 생각보다 단순하지만 한 번 실수하면 반나절이 날아가요. 체크섬은 꼭 남겨두세요!

 

← 이전 글

EP.1 서버 환경 사전 점검

다음 글 →

EP.3 vLLM 실행 및 트러블슈팅 (준비중)

🔧 폐쇄망 Ollama→vLLM 전환기

✔ EP.1 서버 환경 사전 점검
✔ EP.2 컨테이너 이미지 & 모델 반입  ←  현재 글
EP.3 vLLM 실행 및 트러블슈팅
EP.4+ 성능 튜닝 & 운영 (계속 업데이트)

 

반응형

▲ TOP