본문 바로가기
Tech/AI & LLM

(수정)Claude Code + vLLM 폐쇄망 최적 구성 — V100 3장에서 Opus/Sonnet/Haiku 모델 분리하기

by Hoft 2026. 4. 10.

🔄 [2025.04.10 업데이트] 이 글의 모델 추천에 오류가 있었습니다

이 글에서 추천한 Qwen2.5-Coder-14B는 현재 사용 중인 Qwen3-Coder-30B-A3B (Ollama Q4) 대비 코딩 품질이 낮습니다. Qwen3 기술보고서 기준으로 Qwen3-Coder는 Intelligence Index 20점 vs Qwen2.5-14B 수준이며, 세대 차이가 성능 격차를 만듭니다.

오류가 발생한 배경: vLLM에서 Qwen3-Coder-30B-A3B를 올렸을 때 --enforce-eager + PP=3 조합으로 속도가 너무 느려져서 "돌아가는 대안"으로 Qwen2.5-Coder-14B를 선택했는데, 이것이 실제로는 모델 품질 후퇴였습니다.

정정된 권장 구성:
단일~소수 사용자 → Ollama + Qwen3-Coder-30B-A3B 유지가 현재 최선
5명 이상 동시 사용 → vLLM + Qwen3-14B Dense FP16 (Qwen2.5-14B 아님)
자세한 비교 분석: Ollama vs vLLM 동시 사용자 비교 글 참고

Claude Code + vLLM V100 3장 멀티 티어 구성
Tesla V100 × 3장 / Qwen2.5-Coder 14B + 1.5B / Claude Code 멀티 티어 구성

🎯 이 글의 핵심 요약

  • Claude Code의 Opus/Sonnet/Haiku는 사실상 단일 모델에서 동작한다 — 자동 라우팅 없음
  • 최적 구성: Qwen2.5-Coder-14B (GPU 0,1 PP=2) + 1.5B (GPU 2 단독)
  • max-model-len 2048은 즉시 16384로 올려야 — 현재 설정은 파일 편집 불가 수준
  • CLAUDE_CODE_ATTRIBUTION_HEADER=0 설정 누락 시 KV 캐시 무력화로 속도 90% 저하
  • Continue.dev는 chat(8000)과 자동완성(8001)을 포트별로 분리 연결 — 프록시 불필요


🚀 시작하며 — 삽질의 역사

Tesla V100 32GB × 3장, 총 96GB VRAM. 이걸로 vLLM을 올려서 Claude Code를 연결하는 게 목표였는데, 솔직히 말하면 꽤 긴 여정이었습니다. Qwen3-Coder-30B Dense는 너무 느리고, AWQ는 V100 SM7.0에서 커널 자체가 없어서 아예 실패. 결국 Qwen2.5-Coder-15B(사실상 14B), pipeline-parallel-size 3, max-model-len 2048로 어찌어찌 돌아가는 상태까지 왔습니다.

그런데 여기서 멈추기엔 아쉬웠어요. Claude Code에서 ANTHROPIC_DEFAULT_OPUS_MODEL, ANTHROPIC_DEFAULT_SONNET_MODEL, ANTHROPIC_DEFAULT_HAIKU_MODEL을 어떻게 설정해야 하는지, 모델을 어떻게 분리해야 하는지 제대로 분석이 필요했습니다. 이 글은 그 분석 결과를 정리한 실전 레퍼런스입니다.

🔍 Claude Code의 Opus/Sonnet/Haiku — 실제로 어떻게 동작하나?

가장 중요한 사실부터. Claude Code 바이너리 분석(GitHub issue #27665) 결과, 내부 모델 라우팅은 사실상 없다고 봐도 됩니다.

// 실제 라우팅 함수 (단순화)
function getRuntimeMainLoopModel({ permissionMode, mainLoopModel }) {
  // opusplan 모드의 계획 단계에서만 Opus 호출
  if (getUserSpecifiedModel() === "opusplan" && permissionMode === "plan")
    return getDefaultOpusModel();
  // 나머지 모든 작업: 세션에 설정된 단일 모델
  return mainLoopModel;
}

각 티어의 실제 역할을 정리하면 이렇습니다.

OPUS
opusplan 계획 단계
Max 구독자 기본 모델.
opusplan 모드의 계획 단계에서만 호출됨.
SONNET
메인 세션 모델
Pro/Team 기본 모델.
opusplan 실행 단계 + 일반 코딩 작업 전반.
HAIKU
백그라운드 전용
제목 생성, 예시 명령어 제안만.
코딩 작업에는 전혀 관여 안 함.

⚠️ 결론: 로컬 vLLM 환경에서 세 티어에 각각 다른 모델을 매핑하는 건 이론상 가능하지만 실질적 효과가 거의 없습니다. Claude Code가 Haiku를 호출하는 빈도는 극히 낮고, opusplan 모드를 쓰지 않으면 Opus/Sonnet 구분도 무의미합니다. 단일 14B 모델 매핑이 가장 현실적입니다.

📊 V100 3장 VRAM 분석 — PP=3 vs PP=2+1 분리

Claude Code vLLM 시스템 아키텍처 다이어그램
시스템 아키텍처: IDE → vLLM 멀티 인스턴스 → GPU 레이어

현재 구성의 VRAM 사용량 (PP=3)

Qwen2.5-Coder-14B FP16을 GPU 3장에 PP=3으로 올릴 때 실제 VRAM 분포입니다.

구성 요소 GPU 0 GPU 1 GPU 2
모델 가중치 ~10.3 GB ~8.8 GB ~10.3 GB
CUDA/활성화 오버헤드 ~2.0 GB ~2.0 GB ~2.0 GB
KV 캐시 (max-len 2048) 0.13 GB 0.13 GB 0.13 GB
미사용 VRAM ~17 GB ~19 GB ~17 GB

😱 충격적인 사실: KV 캐시에 GPU당 128MB만 사용되고 있습니다. gpu-memory-utilization 0.95로 GPU당 ~18GB를 KV 캐시용으로 확보했는데, 실제 사용량은 그 0.7%에 불과합니다. max-model-len 2048은 심각하게 보수적인 설정입니다.

추천 구성: PP=2 + GPU 2 분리 (2+1 전략)

PP=3 대신 GPU 0,1을 PP=2로 14B 메인 모델에 배치하고, GPU 2는 1.5B 자동완성 전용으로 분리합니다. PP=2는 인터-GPU 통신이 1홉 줄어서 레이턴시가 오히려 개선될 수 있어요.

GPU 모델 역할 포트 max-model-len
GPU 0, 1 Qwen2.5-Coder-14B FP16 메인 코딩 (Opus/Sonnet) :8000 16384
GPU 2 Qwen2.5-Coder-1.5B FP16 자동완성 (Haiku) :8001 32768

💡 왜 PP를 선택하나? NVLink가 없는 PCIe 환경에서 TP(Tensor Parallel)는 all-reduce 통신으로 심각한 병목이 됩니다. PP(Pipeline Parallel)는 스테이지 간 활성화 텐서만 전송하므로 (~40MB/요청) PCIe 15GB/s에서 전송 시간이 ~2.7ms에 불과합니다.

🏆 Opus/Sonnet/Haiku 최적 모델 매핑

Opus Sonnet Haiku 모델 매핑 및 경쟁 모델 비교
Opus/Sonnet/Haiku 역할별 최적 모델 매핑 및 경쟁 모델 비교표

Opus + Sonnet: Qwen2.5-Coder-14B-Instruct (FP16)

Claude Code가 두 티어를 실질적으로 구분하지 않으므로 동일 모델로 매핑합니다. Qwen2.5-Coder-14B는 HumanEval 90+%로 동급 최강 코드 모델이며, 128K 컨텍스트를 지원합니다.

모델 HumanEval Context V100 FP16 비고
Qwen2.5-Coder-14B ✅ 90+% 128K 완전 지원 최선택
Qwen2.5-Coder-7B 88.4% 128K 완전 지원 차선
DeepSeek-Coder-6.7B 73.8% 16K 완전 지원 컨텍스트 짧음
Qwen2.5-Coder-32B 92%+ 128K ⚠️ 매우 느림 3장 독점
CodeLlama-13B ~65% 100K 완전 지원 품질 열등

Haiku: Qwen2.5-Coder-1.5B-Instruct (FP16)

자동완성 전용으로 GPU 2에 단독 배치합니다. 가중치 ~3GB로 VRAM 여유가 넘쳐서 max-model-len 32768까지도 문제없습니다.

🔑 Haiku 모델 선택 포인트
  • Qwen2.5-Coder-1.5B: HumanEval 70.7%, 컨텍스트 32K — 자동완성 FIM 최적
  • Continue.dev 자동완성용은 Base 모델이 Instruct보다 FIM 품질이 높음
  • Claude Code 백그라운드(제목 생성 등)용은 Instruct 모델 권장
  • DeepSeek-Coder-1.3B는 16K 컨텍스트로 열등, 선택 불필요

⚠️ max-model-len 2048 — 파일 편집이 왜 안 되는지

실제 코드 편집에서 얼마나 많은 토큰이 필요한지 분석해보면 2048이 얼마나 부족한지 바로 나옵니다.

작업 유형 필요 토큰 2048으로 가능?
Claude Code 시스템 프롬프트 ~1,500 tok ⚠️ 이미 75% 소진
단일 파일 편집 (짧은 함수) 1,500~3,000 tok ❌ 불가능
멀티파일 리팩토링 3,000~11,000 tok ❌ 완전 불가
코드 리뷰 (diff 포함) 4,000~8,000 tok ❌ 불가능
권장 설정 (16384) ✅ 멀티파일 리팩토링 대부분 처리 가능

📐 VRAM 계산: PP=2 구성에서 GPU당 KV 캐시 잔여 ~14GB. max-model-len 16384 설정 시 GPU당 KV 캐시 소모량은 약 1.5GB에 불과합니다. 2048 → 16384로 바꿔도 VRAM은 전혀 문제없습니다.

⚙️ 최종 설정 — 실전 명령어 모음

vLLM 실행 명령어 및 Claude Code 환경변수 설정
vLLM 멀티 인스턴스 실행 명령어 및 Claude Code / Continue.dev 연동 설정

① vLLM 멀티 인스턴스 시작 스크립트

#!/bin/bash

# ═══════════════════════════════════════════════════
# Instance 1: Qwen2.5-Coder-14B — 메인 코딩 모델
# GPU 0,1 사용 / Pipeline Parallel 2 / Port 8000
# ═══════════════════════════════════════════════════
CUDA_VISIBLE_DEVICES=0,1 \
VLLM_ATTENTION_BACKEND=XFORMERS \
VLLM_USE_V1=0 \
PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True \
NCCL_P2P_DISABLE=1 \
vllm serve /models/Qwen2.5-Coder-14B-Instruct \
    --port 8000 \
    --host 0.0.0.0 \
    --dtype float16 \
    --pipeline-parallel-size 2 \
    --max-model-len 16384 \
    --gpu-memory-utilization 0.92 \
    --enforce-eager \
    --enable-chunked-prefill=False \
    --served-model-name qwen-coder-14b \
    --api-key "local-key-123" \
    --trust-remote-code &

# ═══════════════════════════════════════════════════
# Instance 2: Qwen2.5-Coder-1.5B — 자동완성 전용
# GPU 2 단독 / Port 8001
# ═══════════════════════════════════════════════════
CUDA_VISIBLE_DEVICES=2 \
VLLM_ATTENTION_BACKEND=XFORMERS \
VLLM_USE_V1=0 \
vllm serve /models/Qwen2.5-Coder-1.5B-Instruct \
    --port 8001 \
    --host 0.0.0.0 \
    --dtype float16 \
    --max-model-len 32768 \
    --gpu-memory-utilization 0.90 \
    --enforce-eager \
    --enable-chunked-prefill=False \
    --served-model-name qwen-coder-1.5b \
    --api-key "local-key-123" \
    --trust-remote-code &

echo "✅ vLLM 인스턴스 시작: 14B @ :8000 (GPU 0,1) | 1.5B @ :8001 (GPU 2)"
wait

💡 V100 필수 플래그 설명
--dtype float16 — BF16 미지원 필수 설정
--enforce-eager — V100 CUDA 그래프 문제 방지
--enable-chunked-prefill=False — V100 FP16 chunked prefill 버그 우회 (vLLM issue #11352)
VLLM_USE_V1=0 — vLLM V1 엔진의 V100 비호환 회피
NCCL_P2P_DISABLE=1 — PCIe 환경 NCCL P2P 통신 오류 방지

② Claude Code 설정 (~/.claude/settings.json)

{
  "env": {
    "ANTHROPIC_BASE_URL": "http://localhost:8000",
    "ANTHROPIC_AUTH_TOKEN": "local-key-123",
    "ANTHROPIC_MODEL": "qwen-coder-14b",
    "ANTHROPIC_DEFAULT_OPUS_MODEL": "qwen-coder-14b",
    "ANTHROPIC_DEFAULT_SONNET_MODEL": "qwen-coder-14b",
    "ANTHROPIC_DEFAULT_HAIKU_MODEL": "qwen-coder-14b",
    "CLAUDE_CODE_ATTRIBUTION_HEADER": "0",
    "DISABLE_PROMPT_CACHING": "1",
    "CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC": "1",
    "API_TIMEOUT_MS": "300000"
  }
}

🚨 가장 중요한 설정: CLAUDE_CODE_ATTRIBUTION_HEADER: "0"
이 값이 없으면 Claude Code가 매 요청마다 시스템 프롬프트에 고유 해시를 삽입해서 vLLM의 KV 캐시가 완전히 무효화됩니다. 추론 속도 90% 저하의 원인이 될 수 있어요. 반드시 settings.jsonenv 섹션에만 넣어야 합니다 — export로는 동작하지 않습니다.

③ Continue.dev config.json (~/.continue/config.json)

{
  "models": [
    {
      "title": "Qwen2.5-Coder-14B (Main)",
      "provider": "openai",
      "model": "qwen-coder-14b",
      "apiBase": "http://localhost:8000/v1",
      "apiKey": "local-key-123",
      "contextLength": 16384,
      "completionOptions": {
        "maxTokens": 4096,
        "temperature": 0.1
      }
    }
  ],
  "tabAutocompleteModel": {
    "title": "Qwen2.5-Coder-1.5B (Autocomplete)",
    "provider": "openai",
    "model": "qwen-coder-1.5b",
    "apiBase": "http://localhost:8001/v1",
    "apiKey": "local-key-123",
    "contextLength": 4096,
    "completionOptions": {
      "maxTokens": 256,
      "temperature": 0.0,
      "stop": ["\n\n", "\r\n\r\n"]
    }
  },
  "tabAutocompleteOptions": {
    "debounceDelay": 300,
    "maxPromptTokens": 2048,
    "multilineCompletions": "always"
  }
}

✅ Continue.dev의 장점: tabAutocompleteModel로 chat과 자동완성을 각각 다른 포트에 직접 연결할 수 있습니다. LiteLLM 같은 프록시 없이도 GPU 2의 1.5B 모델이 자동완성 전용으로 동작합니다.


✅ 즉시 해야 할 3가지

1
max-model-len을 2048 → 16384로 올리기

VRAM 여유가 충분합니다. 이것 하나만 해도 파일 편집 품질이 극적으로 달라집니다.

2
PP=3 → PP=2로 변경, GPU 2를 1.5B 자동완성 전용으로 분리

14B 품질은 그대로, 자동완성은 별도 경량 모델이 담당해서 응답 속도 향상.

3
settings.json에 CLAUDE_CODE_ATTRIBUTION_HEADER: "0" 추가

KV 캐시 무효화 방지. 이 설정이 없으면 모든 요청이 콜드 스타트 수준으로 느려집니다.


💬 마치며

V100 3장 폐쇄망 환경이라는 제약이 꽤 컸는데, 결국 AWQ 실패, 30B 속도 문제를 겪고 나서야 "14B PP=2 + 1.5B 단독"이 이 환경에서의 최적해라는 걸 확인했습니다. Claude Code의 Opus/Sonnet/Haiku 라우팅이 생각보다 단순하다는 것도 의외였어요. 처음엔 복잡하게 LiteLLM 프록시까지 구성하려 했는데, 분석해보니 불필요했습니다.

max-model-len 2048에서 파일 편집이 안 되는 분들, 꼭 16384 이상으로 올려보세요. VRAM은 충분하고, 이것 하나가 Claude Code 사용 경험을 완전히 바꿔줍니다.

설정하다가 막히는 부분이나 다른 경험 있으시면 댓글 남겨주세요. 같은 환경에서 고생하시는 분들과 정보 나누고 싶습니다 🙏

📌 관련 참고 자료
  • vLLM 공식 Claude Code 연동 가이드: docs.vllm.ai/en/latest/serving/integrations/claude_code/
  • vLLM V100 chunked-prefill 버그: GitHub issue #11352
  • Claude Code 모델 라우팅 분석: GitHub claude-code issue #27665
  • Continue.dev 공식 문서: docs.continue.dev

※ 이 글은 실제 Tesla V100 3장 폐쇄망 환경에서의 테스트와 오픈소스 커뮤니티 분석을 기반으로 작성되었습니다. 환경에 따라 설정값이 달라질 수 있으므로 VRAM 여유를 확인하며 적용하시기 바랍니다.

 

반응형

▲ TOP