TT Lab
블로그

한국어 English 日本語 中文

시작하기
블로그

블로그

직접 만든 임베딩 서버를 Dynamo 임베딩 워커로 바꿔 끼울 수 있는가

상태: 초안 (2026-11-15), 실측. 이 글의 숫자는 2026-11-15 에 필자가 직접 잰 값입니다. 쿠버네티스 v1.34, Dynamo v1.6.2 의 vllm-runtime:1.6.2 이미지(vLLM 0.31.0), 모델 Qwen3-Embedding-0.6B, 8GB급 노트북 GPU 한 장입니다. 견준 상대는 범용 제품이 아니라 필자가 직접 만든 서버 하나이므로, 숫자를 다른 서버에 그대로 옮기면 안 됩니다. 작성 규칙: 실측 데이터가 기본이고, 못 잰 것은 「미실측」, 확인하지 못한 것은 「미확인」으로 표시합니다.

임베딩 서버를 Dynamo 로 옮겨 보기

목차

1. 왜 임베딩인가

3편과 4편은 시험용 채팅 모델로 쟀습니다. 이번에는 실제로 쓰고 있는 서비스에 Dynamo 를 대 봤습니다. 그런데 쓰고 있는 서비스를 들여다보니 채팅 모델이 아니었습니다. 검색(RAG)에 쓰는 임베딩과 재순위, 그리고 음성 인식을 한 프로세스가 GPU 한 장에서 내주는, 직접 만든 FastAPI 서버였습니다.

이 서버에는 Dynamo 의 대표 기능이 쓸모가 없습니다. 임베딩은 토큰을 생성하지 않으므로 접두 캐시도, KV 라우팅도, 분리 서빙도 해당하지 않습니다. Dynamo 의 코드도 같은 말을 합니다. 임베딩 워커의 도움말에는 KV 이벤트와 KV 라우터 등록을 건너뛴다고 적혀 있습니다(v1.6.2, components/src/dynamo/vllm/backend_args.py).

그래서 물음이 바뀝니다. 직접 만든 서버를 걷어 내고 Dynamo 임베딩 워커로 바꿔 끼울 수 있는가, 그리고 그렇게 하면 무엇이 달라지는가입니다.

2. 구성

Dynamo 의 vLLM 백엔드는 --embedding-worker 인자로 임베딩 전용 워커가 됩니다. vLLM 쪽에는 --runner pooling 을 함께 줍니다.

command: ["python3", "-m", "dynamo.vllm"]
args:
  - --model
  - Qwen/Qwen3-Embedding-0.6B
  - --embedding-worker
  - --runner
  - pooling
  - --max-model-len
  - "1024"
  - --gpu-memory-utilization
  - "0.4"

그래프는 프런트엔드 하나와 워커 하나입니다. 모델은 기존 서버가 쓰는 것과 같은 리비전을 미리 받아 두고, 워커는 HF_HUB_OFFLINE=1 로 띄웠습니다. --max-model-len 1024 는 기존 서버의 입력 한도와 맞춘 값입니다.

견준 순서는 이렇습니다.

  1. 기존 서버에 요청을 보내 벡터와 지연 시간을 모읍니다(3회).
  2. 기존 서버를 내리고, 같은 GPU 에 Dynamo 그래프를 올립니다.
  3. 같은 요청을 Dynamo 프런트엔드에 보냅니다(3회).
  4. 그래프를 내리고 기존 서버를 되살립니다.

요청은 문서 40개(한국어와 영어, 문서 하나에 평균 84토큰)를 8개씩 묶은 것입니다. 기존 서버의 클라이언트가 실제로 8개씩 묶어 보내기 때문입니다. 지연 시간은 순차 30회와 동시 4 로 40회를 쟀고, 3회 반복의 중앙값을 씁니다.

3. 벡터는 같았다

항목 기존 서버 Dynamo 임베딩 워커
차원 1024 1024
첫 벡터의 길이(L2) 1.0 1.0
문서 40개의 토큰 수 합 3,375 3,375
문서별 코사인 유사도 최소 0.9996 · 중앙 0.9998 · 최대 0.9999

토큰 수가 정확히 같고, 벡터는 소수 넷째 자리에서 갈립니다. 기존 서버는 transformers 로 직접 추론하고 Dynamo 쪽은 vLLM 이 추론하므로 연산 순서와 정밀도 처리가 다릅니다. 검색 순위를 바꿀 만한 차이인지는 따로 재지 않았습니다(미실측). 색인을 다시 만들지 않고 섞어 써도 되는지는 이 숫자만으로 단정하지 않습니다.

4. 그대로는 바꿔 끼울 수 없었다

두 서버 모두 /v1/embeddings 를 엽니다. 이름이 같아서 주소만 바꾸면 될 것처럼 보이지만, 같은 요청에 다르게 답한 것이 넷이었습니다.

요청 기존 서버 Dynamo 임베딩 워커
dimensions: 256 200, 256차원 500
input_type: "query" (질의용 지시문) 200, 지시문을 붙여 28토큰 200, 무시하고 9토큰
한도를 넘는 긴 입력 200, 1,024토큰으로 자름 500
encoding_format: "base64" 무시하고 배열 base64 문자열

dimensions. 워커 로그에 까닭이 남았습니다. vLLM 이 이 모델을 Matryoshka 임베딩을 지원하지 않는 모델로 보고, dimensions 가 있으면 요청을 거절합니다. 기존 클라이언트는 모든 요청에 dimensions 를 넣어 보냅니다. 값이 모델의 원래 차원과 같아도 「없어야 한다」는 검사에 걸리므로, 클라이언트를 고치지 않으면 요청이 전부 실패합니다. vLLM 에는 모델을 Matryoshka 로 다루게 하는 재정의 옵션이 있다고 알고 있으나 이 글에서는 시험하지 않았습니다(미실측).

질의용 지시문. 이쪽이 더 위험합니다. 오류가 나지 않기 때문입니다. Qwen3-Embedding 은 질의 앞에 지시문을 붙이도록 만든 모델이고, 기존 서버는 input_type 이나 instruction 필드를 받아 그 일을 대신 합니다. Dynamo 쪽은 모르는 필드를 버리고 글자 그대로 임베딩합니다. 같은 질의로 양쪽이 낸 벡터의 코사인 유사도는 0.82 였습니다. 지시문 없이 견주면 0.9995 입니다. 주소만 바꾸면 모든 요청이 200 으로 돌아오는데 검색 품질은 조용히 떨어집니다. 지시문은 클라이언트가 글자로 붙여서 보내야 합니다.

긴 입력. 기존 서버는 한도에서 자르고, vLLM 은 한도를 넘으면 거절합니다. 어느 쪽이 옳다고 하기는 어렵습니다. 다만 클라이언트가 자르기를 서버에 맡겨 왔다면 그 일을 가져와야 합니다.

오류 코드. dimensions 와 긴 입력은 둘 다 클라이언트의 요청이 잘못된 경우인데, 프런트엔드는 Internal server error 와 함께 500 을 돌려줬습니다. 워커 로그에는 vLLM 의 검증 오류가 까닭과 함께 남아 있었습니다. 500 은 재시도 대상으로 다루는 클라이언트가 많으므로, 이대로면 고칠 수 없는 요청을 계속 다시 보내게 됩니다. v1.6.2 에서 본 동작이고, 최신 판에서 바뀌었는지는 미확인입니다.

5. 지연 시간과 처리량

같은 GPU(8GB급) 에서 잰 값입니다. 묶음 8개 요청 하나의 지연 시간이고, 3회 반복의 중앙값입니다. 오류는 양쪽 모두 0건이었습니다.

순차 p50 순차 p99 동시 4 p50 동시 4 p99 동시 4 처리량
기존 서버 54.3ms 76.0ms 225.9ms 245.9ms 141.0문서/s
Dynamo 임베딩 워커 36.9ms 40.5ms 77.4ms 108.9ms 395.8문서/s

순차 지연은 0.68배, 동시 4 의 처리량은 2.8배입니다. 반복별 처리량은 기존 서버가 139.4·141.0·141.2, Dynamo 가 409.1·395.8·351.9 문서/s 였습니다.

동시 4 에서 벌어지는 까닭은 기존 서버의 구조로 보입니다. 기존 서버는 추론을 전역 잠금 하나로 감싸서 한 번에 한 요청만 처리합니다. 동시 4 의 지연(226ms)이 순차(54ms)의 약 네 배인 것이 그 흔적입니다. vLLM 은 동시에 들어온 요청을 한 배치로 묶습니다. 다만 잠금을 풀거나 배치를 끄고 따로 잰 것은 아니므로, 이 설명은 확인한 것이 아니라 숫자에 맞는 추정입니다.

이 표를 읽을 때 조심할 것이 셋 있습니다.

6. 옮기지 못하는 것

기존 서버가 하는 일은 셋이고, 이 글이 옮겨 본 것은 하나입니다.

기능 Dynamo v1.6.2 의 vLLM 백엔드 이 글에서
임베딩 --embedding-worker 실측
재순위 재순위 핸들러는 SGLang 백엔드 쪽 코드에서만 찾았습니다 미실측
음성 인식 실시간 전사 핸들러가 코드에 있습니다 미실측

임베딩만 옮기면 서버가 하나에서 둘로 늘어납니다. 기존 서버는 세 모델을 한 프로세스에 올려 8GB 카드 하나를 나눠 쓰는데, 임베딩을 따로 떼면 그 카드에 프로세스가 둘이 되고 프런트엔드 파드도 하나 더 생깁니다. 워커가 한 대뿐이라면 Dynamo 프런트엔드가 해 주는 일은 요청을 그 한 대로 넘기는 것뿐입니다.

7. 정리

시리즈의 앞 편들과 이어 보면 이렇습니다. KV 라우팅은 접두가 겹치는 채팅 요청에서 값을 합니다(3편). 분리 서빙은 빠른 망과 큰 모델이 있어야 합니다(4편). 임베딩처럼 생성이 없는 일에는 둘 다 해당하지 않고, 남는 물음은 API 계약이 맞는가입니다(이 편).

참고

로그인하면 좋아요를 누를 수 있습니다

댓글

아직 댓글이 없습니다.

로그인하면 댓글을 쓸 수 있습니다