페이지 목차
이 페이지에서 검색
로컬 LLM
이 페이지에 묻기
로컬 LLM · 현재 차시와 목차 본문에서만 찾아 답합니다. 답을 지어내지 않고 해당하는 대목을 그대로 보여 주며, 여기에 없으면 없다고 알려 드립니다.
생성형 AI가 아니라 이 페이지 본문을 찾아 그대로 보여 주는 방식입니다. 외부로 나가는 요청이 없습니다.
LOCAL · LLM
로컬 LLM
생성 수정
로컬 모델 실행과 GPU·메모리 이해부터 서빙·RAG·도구 호출·업무형 앱 구현까지 차례로 살펴봅니다.
42개 문서 · 3일차 · 20 / 42
19차시 · vLLM 서버 구축
지도 위치 : 3일차 · ③추론 엔진을 ④API로 여는 자리 · 모델 서버 띄우기 | 이전: 18차시 vLLM 환경 구축 | 다음: 20차시 OpenAI 호환 API 활용 키워드 : vLLM Server 실행, 모델 · GPU 설정, Context Length, 서버 구성
이 차시를 다 읽으면
vllm serve한 줄로 서버를 띄우고, 주요 옵션 여섯 개가 각각 무엇을 정하는지 말할 수 있다.- 기동 로그에서 가중치 메모리 · KV 캐시 · "Maximum concurrency" 줄을 찾아 뜻을 말할 수 있다.
- 요청이 하나도 없는데
nvidia-smi가 꽉 차 보이는 것이 왜 정상인지 설명할 수 있다. - 컨텍스트 길이를 크게 잡으면 기동 에러 가 나는 이유를 VRAM 예산식으로 계산할 수 있다.
/health·/v1/models로 서버를 확인하고,--api-key와0.0.0.0의 위험을 말할 수 있다.- 서버를 systemd 또는 Docker 로 상시 실행하는 두 길을 구별할 수 있다.
0. 이 차시의 판별 축
| # | 축 | 왼쪽 (0) | 오른쪽 (1) | 이 축으로 갈리는 것 | 큰 축 (README 5장) |
|---|---|---|---|---|---|
| 1 | 메모리를 언제 잡는가 | 필요할 때 (Ollama 방식) | 기동 시 미리 확보 (vLLM 방식) | nvidia-smi 수치의 뜻 | ① 디스크 ↔ 메모리 |
| 2 | 어디까지 열어 두는가 | 127.0.0.1 (이 기계만) | 0.0.0.0 (모든 네트워크 인터페이스) | 누가 접근할 수 있는가 | ④ 로컬 ↔ 원격 |
| 3 | 누가 프로세스를 지키는가 | 내 터미널 (닫으면 죽음) | systemd · Docker (재부팅해도 다시 뜸) | 상시 실행 | ① 디스크 ↔ 메모리 (꺼지면 사라지는 실행 중 프로세스를 누가 다시 올리는가) |
앞에서 이어집니다 — 17차시 구조도의 모델 서버 칸 을 실제로 세우는 차시입니다. 그리고 9차시의 예산식
필요한 VRAM ≈ 가중치 + KV 캐시 + 여유분이 처음으로 기동 로그 숫자 가 되어 눈앞에 나옵니다.
1. vllm serve — 한 줄로 서버 띄우기
1.1 명령
source ~/vllm-lab/.venv-vllm/bin/activate
export VLLM_API_KEY='여기에_긴_비밀값'
vllm serve Qwen/Qwen3-8B \
--host 127.0.0.1 --port 8000 \
--served-model-name munuibot \
--max-model-len 8192 \
--gpu-memory-utilization 0.90 \
--dtype auto \
--api-key "$VLLM_API_KEY"
🔍 읽는 포인트
- 첫 인자
Qwen/Qwen3-8B— 18차시와 같은 Hugging Face 모델 이름 . 캐시에 있으면 다시 받지 않습니다. --served-model-name munuibot— 클라이언트가 요청에 적을 모델 이름 . 앱 코드에는munuibot만 남고, 뒤의 모델을 바꿔도 앱은 그대로입니다(20차시).--max-model-len·--gpu-memory-utilization— 이 두 줄이 VRAM 예산 을 정합니다(2~4절).
1.2 주요 옵션
| 옵션 | 정하는 것 | 예시값 | VRAM 예산식의 어느 항 | 주의 |
|---|---|---|---|---|
--max-model-len | 요청 하나의 최대 토큰 (입력 + 출력) | 8192 | KV 캐시 (요청 하나당 상한) | 모델 최대치 이하. 너무 크면 기동 에러 (4절) |
--gpu-memory-utilization | vLLM이 쓸 GPU 메모리 비율 | 0.90 | 전체 예산 | 기동 시 미리 확보 (3절) |
--api-key | Authorization: Bearer 키 | 환경변수로 | — | 키는 하나 뿐 (5.2) |
--served-model-name | API에서 부를 모델 이름 | munuibot | — | 클라이언트의 model 값과 정확히 일치해야 함 |
--host / --port | 바인딩 주소 · 네트워크 포트 | 127.0.0.1 / 8000 | — | 0.0.0.0 주의 (5.2) |
--dtype | 가중치 · 계산 정밀도 | auto | 가중치 | auto는 모델 설정(config.json)을 따름. Qwen3는 보통 BF16 |
동시 처리 · 양자화 · KV 캐시 정밀도 · 프리픽스 캐싱 같은 손잡이(--max-num-seqs, --quantization, --kv-cache-dtype, --enable-prefix-caching)는 23차시 에서 다룹니다.
⚠️ 옵션 이름과 기본값은 버전에 따라 바뀝니다. 내 버전의 진실은 vllm serve --help입니다.
1.3 ⚠️ 번역어 — 여기의 "포트"는 네트워크 포트
--port 8000의 포트는 네트워크 포트 입니다. [통신 창구 번호]인데, [ 한 컴퓨터(IP 주소 하나) 안에서 요청이 어느 프로그램에게 가는지 가르는 번호 ]라는 점에서 컴퓨터 자체를 가리키는 IP 주소와 구별됩니다. IP 주소가 건물이라면 포트는 "건물 안의 몇 번 창구"입니다. 5차시에서 본 Ollama의 11434가 같은 것입니다. 20차시의 아키텍처 포트 (계약)와는 같은 철자, 다른 개념입니다(README 6장).
⚠️ 8000번은 관례상 개발용 백엔드 가 많이 쓰는 번호이고, FastAPI(uvicorn)의 기본값도 8000입니다. 같은 기계에서 문의봇 API 서버와 vLLM을 함께 띄우면 포트가 겹칩니다. 하나를 옮기세요(예: FastAPI 8080).
2. ⭐ 기동 로그 읽기
2.1 찾아야 할 줄 세 개
vllm serve를 실행하면 로그가 길게 흐릅니다. 다 읽을 필요 없습니다. 이 세 줄만 찾습니다. (아래는 형태만 보인 것입니다. 문구는 버전마다 조금씩 다르고, 숫자 자리는 일부러 비웠습니다.)
... Model loading took XX.X GiB ... ← ① 가중치가 차지한 메모리
... GPU KV cache size: NN,NNN tokens ← ② KV 캐시 풀의 크기(토큰 수)
... Maximum concurrency for 8192 tokens per request: X.XXx ← ③ 최대 길이 기준 동시 수용량
... Starting vLLM API server ... on http://127.0.0.1:8000
| 줄 | 9차시 예산식의 어느 항 | 이걸로 아는 것 |
|---|---|---|
| ① 가중치 | 가중치 | 9차시에 계산한 값(8B × 2바이트 ≈ 16GB)과 맞는가 |
| ② KV 캐시 풀 | KV 캐시 | 예산에서 가중치와 오버헤드를 빼고 남은 자리 전부 |
| ③ Maximum concurrency | KV 캐시 ÷ (컨텍스트 × 동시 수) | 이 설정으로 최대 길이 요청을 동시에 몇 개 품는가 |
2.2 ⭐ "Maximum concurrency for N tokens per request: X" 의 뜻
X = KV 캐시 풀의 토큰 수 ÷ --max-model-len
모든 요청이 최대 길이(8192 토큰)를 꽉 채운다고 가정할 때, 동시에 몇 개까지 KV 캐시에 들어가는가 입니다.
⚠️ 두 가지로 잘못 읽힙니다.
| 오해 | 바로잡기 |
|---|---|
| "X가 최대 동시 접속자 수다" | ❌ 실제 요청은 대개 최대 길이보다 짧습니다. vLLM은 쓴 만큼만 KV 블록을 잡으므로(21차시) 실제로는 더 많이 들어갑니다. 넘치는 요청은 거절이 아니라 대기열 로 갑니다 |
| "X가 작아도 일단 뜨면 괜찮다" | ⚠️ X가 작다는 것은 긴 요청이 몰릴 때 여유가 적다 는 신호입니다. 1보다 작으면 아예 뜨지 않습니다 (4절) |
2.3 계산으로 미리 짐작하기 — 이론값, 실측 아님
문의봇 조건(RTX 4090 24GB, Qwen3-8B BF16, --max-model-len 8192)으로 9차시 예산식을 돌려 봅니다. 층 수 · KV 헤드 수 · head_dim은 모델의 config.json에서 확인 합니다.
GPU 24GB × 0.90 ≈ 21.6 GB ← vLLM이 확보하는 예산
− 가중치 (약 8.2B × 2바이트) ≈ 16.4 GB
− 활성값 · CUDA 등 오버헤드 ≈ 1~2 GB
= KV 캐시 풀 ≈ 3.2 ~ 4.2 GB
÷ KV 캐시/토큰 (2 × 36층 × 8헤드 × 128 × 2바이트) ≈ 144 KB
= 풀에 들어가는 토큰 ≈ 2.2만 ~ 2.8만 토큰
÷ 8192 ≈ 동시 2.7 ~ 3.5개 (최대 길이 기준)
⭐ 상담원 10명 을 생각하면 여유가 넉넉하지 않습니다. 실제 문의는 짧아서 더 들어가겠지만, 이 계산이 23차시의 출발점입니다. 그때 손잡이는 넷입니다. 컨텍스트를 줄이거나, 가중치를 가볍게 하거나(양자화 모델), KV 캐시를 가볍게 하거나(FP8), 예산 비율을 올리거나. 로그의 실제 숫자와 이 계산을 나란히 놓는 것 이 이 차시 실습의 핵심입니다.
3. ⭐ nvidia-smi가 꽉 차 보이는 것은 정상입니다
--gpu-memory-utilization 0.90은 "GPU 메모리의 90%까지 쓸 수 있다"가 아니라 "기동할 때 90%를 미리 확보한다" 입니다. 가중치를 올리고, 남은 자리를 전부 KV 캐시 풀로 예약 해 둡니다. 그래서 요청이 0개여도 nvidia-smi의 memory.used가 거의 꽉 차 보입니다.
| Ollama (5차시) | vLLM | |
|---|---|---|
| 언제 잡나 | 모델을 처음 부를 때 (가중치 + num_ctx 만큼의 KV) | 기동할 때 한 번에 |
| 언제 놓나 | 마지막 요청 후 기본 5분 뒤 언로드 | 서버가 떠 있는 동안 놓지 않음 |
nvidia-smi 모양 | 요청에 따라 오르내림 | 평평하게 높음 |
앞에서 배운 축 ① 디스크 ↔ 메모리 에서, vLLM은 "필요할 때 빌리기"가 아니라 "자리를 먼저 통째로 잡아 두기" 를 고른 것입니다. 요청이 올 때마다 메모리를 구하러 다니지 않으니 빠르고 예측 가능합니다. 대신 남과 나눠 쓰기 어렵습니다.
⚠️ 여기서 세 가지가 따라옵니다.
nvidia-smi로는 "실제로 얼마나 쓰고 있는가"를 알 수 없습니다. KV 캐시 실사용률은/metrics에서 봅니다(22차시).- 같은 GPU에 다른 프로세스를 두기 어렵습니다 (18차시 5.1). 꼭 함께 써야 하면 비율을 낮추고, 그만큼 KV 풀이 줄어드는 것을 받아들입니다.
- 24차시에서 Ollama와 메모리를 비교할 때
nvidia-smi숫자를 그대로 비교하면 틀립니다.
💡 비유는 기억 보조로만 — 손님이 올 때마다 의자를 가져오는 식당(Ollama)과, 개점 전에 홀 전체를 세팅해 두는 식당(vLLM).
4. 컨텍스트 길이가 너무 크면 — 기동 에러
--max-model-len을 생략하면 vLLM은 모델이 지원하는 최대치 (config.json의 값, Qwen3-8B는 수만 토큰 — 직접 확인)를 씁니다. 2.3의 계산에서 KV 풀이 2.2만 ~ 2.8만 토큰이었습니다. 최대치가 그보다 크면 최대 길이 요청 하나조차 KV 캐시에 못 담습니다. vLLM은 이 상태로 뜨지 않고 기동 단계에서 멈춥니다.
에러 문구는 버전마다 다르지만 취지는 이렇습니다. "최대 시퀀스 길이가 KV 캐시에 저장할 수 있는 토큰 수보다 크다. gpu_memory_utilization을 올리거나 max_model_len을 줄여라."
| 손잡이 | 효과 | 대가 | 자세히 |
|---|---|---|---|
--max-model-len 줄이기 | 요청 하나당 상한↓ → X↑ | 긴 메일은 거절되거나 앞에서 잘라야 함 | 23차시 |
--gpu-memory-utilization 올리기 (예: 0.95) | 예산↑ | 여유분이 줄어 다른 메모리 사용과 부딪힐 수 있음 | 23차시 |
| 양자화 모델 (AWQ · FP8 등) | 가중치↓ → KV 풀↑ | 품질 확인 필요 (15차시) | 23차시 |
--kv-cache-dtype fp8 | 토큰당 KV↓ | 품질 확인 필요 | 23차시 |
⚠️ 2차시 3절의 구분을 다시 봅니다. 모델이 지원하는 최대치 ≠ 내가 설정해서 쓰는 값. 문의봇은 "모델이 몇까지 되나"가 아니라 "우리 메일이 얼마나 긴가" 로 정합니다(23차시 3절).
5. 서버 확인과 보안
5.1 살아 있는지, 무엇이 떠 있는지
curl -s -o /dev/null -w "%{http_code}\n" http://127.0.0.1:8000/health
curl -s http://127.0.0.1:8000/v1/models -H "Authorization: Bearer $VLLM_API_KEY"
curl -s -o /dev/null -w "%{http_code}\n" http://127.0.0.1:8000/v1/models # 키 없이
curl -s http://127.0.0.1:8000/v1/chat/completions \
-H "Content-Type: application/json" -H "Authorization: Bearer $VLLM_API_KEY" \
-d '{"model":"munuibot","messages":[{"role":"user","content":"주문한 지 일주일째 안 와요. 한 줄로 요약해줘."}],"max_tokens":100}'
🔍 읽는 포인트
/health→ 200 이면 살아 있음. 17차시 2.4에서 말한 허브의 헬스 체크 가 이것입니다./v1/models→ 응답의"id"가munuibot이어야 합니다.- 키 없이 부르면 401 이 나와야 정상입니다. HTTP 상태 코드 401 = 인증 실패 (Unauthorized, "누구인지 확인할 수 없다", 17차시 2.3).
- 마지막 요청의 응답 구조(
choices,usage,finish_reason)는 20차시에서 해부합니다.
5.2 ⚠️ --api-key와 0.0.0.0
--host는 서버의 바인딩 주소 , 즉 "어느 주소로 들어오는 요청을 받을 것인가"를 정합니다. 앞에서 배운 대로(5차시 4.2) 두 값이 갈립니다.
| 바인딩 주소 | 뜻 | 접근할 수 있는 곳 |
|---|---|---|
127.0.0.1 | 루프백 — 이 컴퓨터가 자기 자신을 가리키는 주소 | 같은 기계 안의 프로그램만 |
0.0.0.0 | 이 기계가 가진 모든 네트워크 인터페이스 (유선 · 무선 · 사설망 주소 전부) | 같은 네트워크의 다른 PC도 서버IP:8000으로 |
여기에 인증 (누구인지 확인, 17차시 2.3)이 있느냐가 겹치면 아래 표가 됩니다.
| 구성 | 접근할 수 있는 사람 | 판정 |
|---|---|---|
127.0.0.1 + 키 | 같은 기계의 프로세스 (API 서버가 같은 기계일 때) | ⭐ 기본값으로 |
0.0.0.0 + 키 | 같은 네트워크의 키를 아는 누구나 | API 서버가 다른 기계일 때만, 사설망 · 방화벽과 함께 |
0.0.0.0 + 키 없음 | 같은 네트워크의 누구나 | ❌ GPU와 모델을 통째로 공개 |
0.0.0.0으로 여는 순간 같은 네트워크 전체가 손님 이 됩니다. 테스트하려고 잠깐 연 한 줄이 그대로 보안 구멍이 되는 흔한 경로입니다. 6.2의 Docker 포트 연결도 같은 함정을 품고 있습니다.- 5차시의
OLLAMA_HOST=0.0.0.0은 인증 없이 열렸습니다. vLLM은 키를 걸 수 있지만, 키 하나 = 서비스 간 인증 일 뿐 상담원별 권한은 없습니다(17차시 2.3). - 키를 명령줄에 직접 쓰면 셸 기록과 프로세스 목록에 남습니다. 환경변수나 파일 로 넘깁니다. vLLM은
VLLM_API_KEY환경변수도 읽습니다(버전 확인).
6. 상시 실행 — systemd 또는 Docker
터미널에서 띄운 서버는 터미널을 닫으면 같이 죽습니다. 서버가 죽거나 기계가 재부팅되면 누군가 다시 띄워야 합니다. 운영에서는 그 "누군가"를 사람이 아니라 OS나 컨테이너 엔진에게 맡깁니다.
6.1 systemd 서비스로
systemd — [리눅스의 서비스 관리자]인데, [ 프로그램을 서비스로 등록해 두면 부팅 때 자동으로 띄우고, 죽으면 다시 띄우고, 로그를 모아 준다 ]는 점에서 내 터미널에서 직접 띄운 프로세스와 구별된다. 서비스 하나의 실행 방법을 적은 설정 파일이 유닛 파일 (.service)이고, systemctl이 그 서비스를 켜고 끄는 명령, journalctl이 로그를 보는 명령입니다.
Ollama는 설치할 때 이미 systemd 서비스로 등록되었고, 그 설정을 systemctl edit로 바꿨습니다(5차시 1.2 · 4.1). vLLM은 유닛 파일을 직접 만듭니다.
# /etc/systemd/system/vllm.service
[Unit]
Description=vLLM server (munuibot)
After=network-online.target
[Service]
User=llm
EnvironmentFile=/etc/vllm/vllm.env
ExecStart=/home/llm/vllm-lab/.venv-vllm/bin/vllm serve Qwen/Qwen3-8B \
--host 127.0.0.1 --port 8000 --served-model-name munuibot \
--max-model-len 8192 --gpu-memory-utilization 0.90
Restart=on-failure
[Install]
WantedBy=multi-user.target
# /etc/vllm/vllm.env 에는 VLLM_API_KEY=..., HF_HOME=... 처럼 값만 둔다
sudo systemctl daemon-reload
sudo systemctl enable --now vllm
journalctl -u vllm -f # 2절의 기동 로그를 여기서 읽는다
🔍 읽는 포인트
ExecStart— 가상환경 안의vllm실행 파일을 절대 경로로 부릅니다. activate 없이도 그 환경이 쓰입니다.EnvironmentFile— 키를 유닛 파일 밖의 환경 파일 에 둡니다. 이것이 흔히.env라고 부르는 방식입니다..env파일 은 [설정 파일]인데, [ 비밀값과 환경마다 달라지는 값을이름=값줄로 적어 코드 · 설정 본문 밖에 둔다 ]는 점에서 코드에 값을 직접 쓰는 방식과 구별됩니다. 규칙은 셋입니다. ① 비밀값은 코드에 쓰지 않는다 ② 값이 든 파일은 Git에 올리지 않는다 ③ 대신 키 이름만 적은.env.example을 올린다(20차시 3.3).Restart=on-failure— 죽으면 다시 띄웁니다.enable은 재부팅 후 자동 시작.
6.2 Docker 컨테이너로
| 용어 | 정의 |
|---|---|
| Docker 이미지 | [실행 환경 꾸러미]인데, [ 앱과 그 앱이 쓰는 OS 라이브러리까지 한 묶음으로 굳혀 버전 태그를 붙인다 ]는 점에서 파이썬 패키지만 격리하는 가상환경과 구별된다 |
| 컨테이너 | [격리된 프로세스]인데, [ 이미지를 실행한 것 ]이라는 점에서 디스크에 저장된 이미지와 구별된다. 이미지 : 컨테이너 = 프로그램 파일 : 실행 중인 프로그램 (축 ①) |
docker run --gpus all --ipc=host \
-p 127.0.0.1:8000:8000 \
-v ~/.cache/huggingface:/root/.cache/huggingface \
--env-file ./vllm.env \
vllm/vllm-openai:<버전태그> \
--model Qwen/Qwen3-8B --served-model-name munuibot --max-model-len 8192
🔍 읽는 포인트
--gpus all— 컨테이너는 기본적으로 GPU를 보지 못합니다. 호스트(컨테이너를 돌리는 실제 기계)에 NVIDIA Container Toolkit (컨테이너에 GPU를 연결해 주는 NVIDIA 도구)이 설치되어 있어야 이 옵션이 동작합니다.--ipc=host— 컨테이너가 호스트의 공유 메모리를 쓰게 합니다. vLLM(PyTorch)이 프로세스 사이에 공유 메모리를 쓰기 때문에 공식 문서가 이 옵션(또는 넉넉한--shm-size)을 권합니다.-p 127.0.0.1:8000:8000—호스트 주소:호스트 포트:컨테이너 포트로 바깥 창구와 컨테이너 안 창구를 잇습니다. 앞에127.0.0.1을 붙이지 않으면0.0.0.0에 열립니다(5.2).-v ...huggingface...— 모델 캐시를 볼륨 (컨테이너 밖의 디스크 폴더를 컨테이너 안에 붙이는 것)으로 연결해, 컨테이너를 지워도 모델을 다시 받지 않게 합니다.<버전태그>—latest대신 버전을 고정 합니다. 이미지 이름과 인자 형식은 공식 문서의 Docker 배포 페이지에서 확인합니다.
| systemd + 가상환경 | Docker | |
|---|---|---|
| 격리 강도 | 파이썬 패키지만 | 앱 + OS 라이브러리 |
| 버전 고정 | 가상환경 재현이 손에 달림 | 이미지 태그 하나로 고정 |
| 재시작 · 부팅 | Restart=, enable | --restart, 또는 Podman(Docker와 같은 컨테이너 표준을 따르는 다른 엔진) + systemd |
| 어울리는 곳 | 장비 한 대, 빠른 실험 | 여러 장비에 같은 환경 배포, 5일차 프로젝트 |
7. 실습 — 맡기고 검수하기
지시
18차시에 만든 .venv-vllm 환경으로 vLLM 서버를 띄우고 확인해줘.
맥락: vllm serve --help 출력과 공식 문서(docs.vllm.ai)의 OpenAI-compatible server 페이지를 근거로 할 것.
제약:
- --host 127.0.0.1, --port 8000, --served-model-name munuibot, API 키는 환경변수 VLLM_API_KEY로.
- 0.0.0.0으로 열지 마. systemd 등록은 유닛 파일 내용을 먼저 보여주고 내 승인 후에.
할 일:
1. --max-model-len 8192로 기동. 기동 로그에서 가중치 메모리 / KV 캐시 크기 / Maximum concurrency 줄을 원문 그대로 뽑아줘.
2. config.json에서 max_position_embeddings 값을 확인하고, --max-model-len을 그 값으로 바꿔 다시 기동. 실패하면 에러 원문을.
3. 요청이 0개일 때 nvidia-smi --query-gpu=memory.used,memory.total --format=csv 출력.
4. /health, /v1/models(키 있음/없음), /v1/chat/completions 한 건의 HTTP 코드와 응답 원문.
5. ss -ltnp | grep 8000 출력.
보고 형식: [항목 | 실행한 명령 | 출력 원문 | 한 줄 해석] 표.
추측하지 말고 실행 출력 원문을 붙여.
받을 증거
- 기동 로그 세 줄 원문 (8192일 때, 최대치일 때 각각)
nvidia-smi메모리 한 줄,curl결과 네 개,ss한 줄
내가 직접 볼 것
| 볼 것 | 이걸로 아는 것 |
|---|---|
| Maximum concurrency 값 | 2.3 계산(2.7 ~ 3.5개 근처)과 비슷한가. 크게 다르면 조건(정밀도 · 오버헤드)이 다른 것 |
| 최대치로 띄웠을 때의 결과 | 4절의 기동 에러가 실제로 나는가, 에러가 무엇을 하라고 하는가 |
| 요청 0개일 때 memory.used | 전체의 90% 근처면 정상 (3절) |
키 없는 /v1/models | 401 이어야 함 |
/v1/models의 id | munuibot |
ss 출력의 주소 | 127.0.0.1:8000 (❌ 0.0.0.0:8000) |
부록 A. 스스로 확인하기
| # | 질문 | 답이 있는 곳 |
|---|---|---|
| 1 | --max-model-len과 --gpu-memory-utilization은 VRAM 예산식의 어느 항을 정하는가? | 1.2 |
| 2 | --served-model-name을 쓰면 앱 쪽에 어떤 이득이 있는가? | 1.1 |
| 3 | 기동 로그에서 찾을 세 줄과 각각의 뜻은? | 2.1 |
| 4 | "Maximum concurrency: X"는 최대 동시 접속자 수인가? | 2.2 |
| 5 | 24GB에서 Qwen3-8B BF16, 8192 토큰이면 X는 대략 얼마로 짐작되는가? | 2.3 |
| 6 | 요청이 없는데 nvidia-smi가 꽉 찬 이유는? | 3 |
| 7 | --max-model-len을 생략하면 왜 기동 에러가 날 수 있는가? | 4 |
| 8 | 0.0.0.0 + 키 없음이 위험한 이유를 바인딩 주소와 인증으로 설명하면? | 5.2 |
| 9 | systemd를 유와 종차로 정의하면? 유닛 파일에서 ExecStart가 가상환경의 절대 경로를 쓰는 이유는? | 6.1 |
| 10 | Docker 이미지와 컨테이너는 무엇으로 갈리는가? systemd + 가상환경과 비교하면 무엇을 더 격리하는가? | 6.2 |
부록 B. 자주 하는 실수
| 실수 | 바로잡기 |
|---|---|
| "요청도 없는데 메모리를 90% 쓴다 → 메모리 누수" | ❌ 기동 시 미리 확보 . 정상 |
| "Maximum concurrency가 3이니 동시 접속 3명까지" | ❌ 최대 길이 가정의 값. 실제 요청이 짧으면 더 들어가고, 넘치면 대기열로 |
--max-model-len 생략 | ⚠️ 모델 최대치가 적용되어 기동 실패하거나 동시 수용량이 크게 줄 수 있음 |
테스트하려고 --host 0.0.0.0 | ⚠️ 같은 네트워크 전체에 열림. 127.0.0.1이 기본 |
| API 키를 명령줄에 직접 | ⚠️ 셸 기록 · 프로세스 목록에 남음. 환경변수 · 파일로 |
| FastAPI와 vLLM을 둘 다 8000에 | ❌ 네트워크 포트 충돌. 하나를 옮김 |
Docker 이미지를 latest로 | ⚠️ 다음 실행 때 다른 버전이 뜰 수 있음. 태그 고정 |
부록 C. NotebookLM 복습 프롬프트
이 19차시 노트만 근거로 6문제를 내줘. 비전공자 기준으로.
"Maximum concurrency 줄의 뜻", "nvidia-smi가 꽉 차 보이는 것이 정상인 이유", "컨텍스트 길이가 크면 기동 에러가 나는 이유", "0.0.0.0의 위험"은 반드시 포함해줘.
답은 내가 답한 다음에 채점하고, 틀린 이유를 노트의 절 번호를 근거로 설명해줘.
LangTailor