vLLM OpenAI 호환 서버 운영, API 키·모델 이름·프록시 경계를 나누는 법

로컬 AI 추론 서버와 GPU 컴퓨팅 장비를 점검하는 기술 작업 환경

AI를 활용하여 생성한 이미지입니다.

vLLM을 로컬이나 사내 서버에서 OpenAI 호환 API로 열면, 기존 SDK와 클라이언트를 그대로 연결하기 쉬워요.[1] 다만 --api-key를 붙였다고 서버 전체가 잠기는 것은 아닙니다.[1] vLLM 공식 문서는 API 키가 일부 경로에만 적용되고, 같은 서버의 다른 경로에는 인증이 없을 수 있다고 명시해요.[1] 중급자라면 실행 명령보다 모델 이름, 엔드포인트, 인증 범위, 프록시 경계를 먼저 설계해야 합니다.[1]

먼저 정할 네 가지

첫째는 실제 모델 경로예요.[1] vLLM의 --model은 Hugging Face 모델 이름이나 로컬 경로가 될 수 있고, 별도 이름을 지정하지 않으면 메트릭의 모델 이름에도 사용됩니다.[2]

둘째는 클라이언트가 보낼 모델 이름입니다.[1] 서버가 실제 디렉터리 이름을 그대로 노출하면 클라이언트 설정과 배포 경로가 묶여요.[1] --served-model-name으로 외부에서 사용할 안정적인 이름을 정하면 모델 파일을 교체할 때 클라이언트 설정을 덜 건드릴 수 있습니다.[1] 이 이름은 요청 JSON의 model 값과 맞아야 해요.[1]

셋째는 바인딩 주소예요. 같은 PC의 앱만 쓸 때 127.0.0.1에 묶으면 LAN에서 직접 접근할 수 없습니다.[1] 다른 PC나 컨테이너에서 호출해야 한다면 0.0.0.0을 고려할 수 있지만, 그 순간 방화벽과 역방향 프록시가 보안 경계가 됩니다.[1]

넷째는 인증 위치예요. vLLM 프로세스의 API 키는 애플리케이션 계층의 한 겹일 뿐이고, 공개 인터넷에 포트를 바로 열어 주는 방화벽 규칙을 대신하지 못합니다.[1]

가장 작은 단일 호스트 구성

테스트용으로는 외부에 노출하지 않고 로컬에서만 확인하세요.[1] 아래 명령은 예시이며 모델 이름과 경로는 실제 설치 환경에 맞춰 바꿔야 해요.[1]

vllm serve /models/my-instruct-model \
  --host 127.0.0.1 \
  --port 8000 \
  --served-model-name local-instruct \
  --api-key "$VLLM_API_KEY"

공식 서버는 OpenAI Completions API와 Chat Completions API를 포함한 HTTP 인터페이스를 제공합니다.[1] Chat Completions는 채팅 템플릿이 있는 텍스트 생성 모델에 적용되므로, 모델에 템플릿이 없는데 /v1/chat/completions부터 호출하면 모델 로딩이 아니라 요청 형식에서 막힐 수 있어요.[1]

서버가 뜬 뒤에는 먼저 모델 목록을 확인하세요.[1]

curl -sS http://127.0.0.1:8000/v1/models \
  -H "Authorization: Bearer $VLLM_API_KEY"

응답에서 id가 local-instruct인지 확인한 다음, 그 값을 그대로 요청에 넣습니다.[1]

curl -sS http://127.0.0.1:8000/v1/chat/completions \
  -H "Authorization: Bearer $VLLM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "local-instruct",
    "messages": [{"role": "user", "content": "짧게 자기소개해 주세요."}],
    "temperature": 0.2,
    "max_tokens": 128
  }'

여기서 확인할 것은 응답 문장보다 먼저 HTTP 상태, model 값, 오류의 위치예요.[1] 401이면 키나 헤더 문제이고, 404면 경로·포트·프록시 문제일 가능성이 큽니다.[1] 400이면서 채팅 템플릿이나 모델 이름을 언급하면 서버는 살아 있지만 요청 계약이 맞지 않는 상황이에요.[1]

API 키의 보호 범위를 오해하지 않기

vLLM 문서의 현재 안내에서 --api-key 또는 VLLM_API_KEY는 /v1, /v2, /inference 경로 아래 요청을 인증하지만, 같은 HTTP 서버의 다른 엔드포인트는 인증되지 않을 수 있습니다.[1] 특히 /invocations가 같은 추론 능력을 노출할 수 있으므로 API 키만으로 서버 전체를 보호한다고 보면 안 돼요.[1]

따라서 서버를 다른 장치에서 쓸 때는 다음처럼 역할을 나누세요.[1]

  • vLLM은 사설 인터페이스에만 바인딩하거나 내부 네트워크에 둬요.
  • 외부에서 들어오는 TLS, IP 제한, 사용자 인증은 역방향 프록시에서 처리해요.
  • 프록시가 허용할 경로를 /v1/처럼 필요한 범위로 제한해요.
  • /metrics, /health, /invocations처럼 운영에 필요한 경로도 인증·접근 허용 범위를 따로 정해요.
  • API 키를 셸 히스토리나 저장소에 직접 적지 말고 환경 변수와 시크릿 저장소를 사용해요.

공식 문서도 API 키의 제한을 설명하면서 역방향 프록시 뒤에 배치하는 방식 같은 추가 보강을 권고합니다.[1] 회사 내부 서버라도 사내망이면 안전하다고 단정하지 마세요.[1] 로그와 프록시 설정에 Authorization 헤더가 남지 않는지도 확인해야 해요.[1]

모델 이름을 고정하는 이유

모델을 /models/llama-3.1-8b에서 /models/llama-3.1-8b-instruct-q8로 교체하면, 클라이언트가 실제 경로를 모델 이름으로 사용하던 구성은 함께 깨집니다.[1] 외부 계약을 local-instruct처럼 짧게 두면 파일 경로와 API 계약을 분리할 수 있어요.[1]

다만 같은 서버에서 여러 모델을 제공하려고 이름만 여러 개로 만들 수 있다고 생각하면 곤란합니다.[1] 서버가 실제로 로드한 모델과 요청의 model 값이 어떻게 매핑되는지 확인해야 하고, 모델별 토크나이저와 채팅 템플릿이 다르면 같은 프롬프트가 같은 결과 형식을 보장하지 않을 수 있어요.[1] vLLM 공식 옵션 문서도 --model이 모델 설정의 중심이 되고, 별도 served_model_name이 없을 때 모델 이름으로 사용된다고 설명합니다.[2]

장애를 빠르게 나누는 점검 순서

1. 프로세스와 포트

curl -i http://127.0.0.1:8000/health
lsof -nP -iTCP:8000 -sTCP:LISTEN

포트가 열렸는지부터 확인하세요. 모델 로딩이 끝나지 않았는데 클라이언트 재시도만 늘리면 GPU 메모리와 로그가 더 복잡해집니다.[1]

2. 인증과 경로

/v1/models에 키를 넣었을 때 200, 키를 빼면 401인지 확인합니다.[1] 두 요청이 똑같이 200이면 프록시나 서버 설정이 인증 헤더를 전달하지 않거나, 호출한 경로가 인증 대상이 아닐 수 있어요.[1] 키가 맞는데 401이라면 환경 변수가 vLLM 프로세스에 실제로 전달됐는지 확인하세요.[1]

3. 모델 계약

/v1/models의 id와 요청 JSON의 model이 같은지 확인합니다.[1] 모델 이름이 다르면 GPU 추론까지 가지 않고 요청 검증에서 끝납니다.[1] Chat Completions를 쓸 때는 모델의 chat template 지원 여부도 같이 확인해야 해요.[1]

4. 응답 형식과 도구 호출

vLLM 문서에 따르면 parallel_tool_calls를 false로 설정하면 요청당 도구 호출을 0개 또는 1개로 제한할 수 있고, true여도 모델에 따라 여러 호출이 보장되지는 않습니다.[1] 도구 호출을 쓰는 앱이라면 이 값을 모델 능력과 함께 테스트하세요.[1] API가 OpenAI 형식이라는 말이 모든 옵션의 의미와 동작까지 완전히 같다는 뜻은 아닙니다.[1]

프록시 뒤에 둘 때의 최소 설계

프록시와 vLLM을 한 서버에 두더라도 포트를 두 개로 나누면 확인이 쉬워요.[1] vLLM은 127.0.0.1:8000, 프록시는 TLS가 적용된 외부 포트에서 받고, 프록시가 Authorization을 내부 upstream에 전달합니다.[1] 외부에서는 /v1/models와 필요한 추론 경로만 열고, 관리·진단 경로는 내부 IP나 별도 인증으로 제한하세요.[1]

프록시를 추가한 뒤에는 내부 포트와 외부 포트를 각각 테스트해야 합니다.[1] 내부에서는 vLLM 키가 작동하는지, 외부에서는 프록시의 사용자 인증과 업스트림 전달이 모두 작동하는지 분리해 보면 401의 위치를 빨리 찾을 수 있어요.[1]

운영 전에 남겨 둘 체크리스트

  • 모델 경로와 외부 모델 이름을 문서에 분리해 적어요.
  • 로컬 테스트는 127.0.0.1로 시작하고, LAN 노출은 필요한 경우에만 켜요.
  • /v1/models에서 서버가 실제로 광고하는 id를 읽어 요청에 사용해요.
  • --api-key가 모든 경로를 보호하지 않는다는 전제로 프록시와 방화벽을 구성해요.
  • Chat Completions를 쓴다면 chat template과 도구 호출 동작을 별도로 검증해요.
  • 키가 로그·셸 히스토리·클라이언트 오류 화면에 남지 않는지 확인해요.

vLLM을 OpenAI 호환 서버로 쓰는 핵심은 명령 한 줄을 외우는 데 있지 않아요.[1] 모델 경로, 외부 이름, 요청 경로, 인증 경계를 각각 확인해야 모델을 바꾸거나 클라이언트를 늘릴 때 문제가 줄어듭니다.[1]

Sources

[1] https://docs.vllm.ai/en/latest/serving/online_serving/openai_compatible_server — vLLM OpenAI-Compatible Server [2] https://docs.vllm.ai/en/latest/configuration/engine_args — vLLM engine arguments

함께 읽을 글