Llama 3.1 8B·70B를 Lambda Cloud 온디맨드 인스턴스로 서빙하기

Llama 3.1 8B·70B를 Lambda Cloud 온디맨드 인스턴스로 서빙하기

단일 GPU 인스턴스 하나로도 비교적 가벼운 Llama 3.1 모델은 손쉽게 서빙할 수 있어요. 이 가이드에서는 Lambda Cloud의 1x 또는 8x A100/H100 온디맨드 인스턴스 위에서, Docker 컨테이너 안의 vLLM으로 Llama 3.1 8B와 70B 모델을 서빙하는 방법을 보여줘요.

가상환경을 직접 구성하는 대신 vllm/vllm-openai 이미지를 그대로 쓰기 때문에, 모델 서버를 띄우고 curl로 확인하는 절차가 아주 간결해요. 이 페이지를 읽고 나면 8B·70B 각각에 맞는 docker 명령의 차이와 텐서 병렬 설정을 파악할 수 있어요.

출처: Serving the Llama 3.1 8B and 70B models using Lambda Cloud on-demand instances - Lambda Docs

vLLM API 서버 시작

아직 인스턴스를 안 띄웠다면 콘솔이나 Cloud API로 인스턴스를 시작하고, SSH로 접속해요.

먼저 환경변수를 설정해요.

export HF_TOKEN=HF-TOKEN HF_HOME="/home/ubuntu/.cache/huggingface" MODEL_REPO=meta-llama/MODEL
  • HF-TOKEN: Hugging Face User Access Token으로 바꿔주세요.
  • MODEL: 서빙하려는 모델에 따라 이렇게 바꿔요.
    • 8B 모델을 서빙한다면: Meta-Llama-3.1-8B-Instruct
    • 70B 모델을 서빙한다면: Meta-Llama-3.1-70B-Instruct

70B 모델은 8x A100 또는 H100이 필요해요.

tmux 세션을 시작해요. 8B 모델을 서빙한다면 아래 명령을 실행해요.

sudo docker run \
  --gpus all \
  --ipc=host \
  -v "${HF_HOME}":/root/.cache/huggingface \
  -p 8000:8000 \
  --env "HUGGING_FACE_HUB_TOKEN=${HF_TOKEN}" \
  vllm/vllm-openai --model "${MODEL_REPO}" \
    --disable-log-requests

70B 모델을 8x A100/H100 인스턴스(필수)에서 서빙한다면, 대신 아래 명령을 사용해요.

sudo docker run \
  --gpus all \
  --ipc=host \
  -v "${HF_HOME}":/root/.cache/huggingface \
  -p 8000:8000 \
  --env "HUGGING_FACE_HUB_TOKEN=${HF_TOKEN}" \
  vllm/vllm-openai --model "${MODEL_REPO}" \
    --disable-log-requests \
    --tensor-parallel-size 8

두 명령 모두 (1) 서빙할 모델을 내려받고, (2) 선택한 모델로 vLLM의 API 서버를 시작해요.

두 명령의 차이는 두 번째 명령이 텐서 병렬 전략을 활성화해 8개 GPU를 쓴다는 점이에요. 분산 추론 전략에 대한 더 자세한 내용은 vLLM 문서를 참고하세요.

vLLM API 서버가 올라오면 아래처럼 사용 가능한 라우트 목록이 보여요. 여기서 /v1/chat/completions, /v1/completions, /v1/embeddings 같은 OpenAI 호환 엔드포인트가 열린 걸 확인할 수 있어요.

INFO 08-01 19:11:07 api_server.py:292] Available routes are:
INFO 08-01 19:11:07 api_server.py:297] Route: /openapi.json, Methods: GET, HEAD
INFO 08-01 19:11:07 api_server.py:297] Route: /docs, Methods: GET, HEAD
INFO 08-01 19:11:07 api_server.py:297] Route: /docs/oauth2-redirect, Methods: GET, HEAD
INFO 08-01 19:11:07 api_server.py:297] Route: /redoc, Methods: GET, HEAD
INFO 08-01 19:11:07 api_server.py:297] Route: /health, Methods: GET
INFO 08-01 19:11:07 api_server.py:297] Route: /tokenize, Methods: POST
INFO 08-01 19:11:07 api_server.py:297] Route: /detokenize, Methods: POST
INFO 08-01 19:11:07 api_server.py:297] Route: /v1/models, Methods: GET
INFO 08-01 19:11:07 api_server.py:297] Route: /version, Methods: GET
INFO 08-01 19:11:07 api_server.py:297] Route: /v1/chat/completions, Methods: POST
INFO 08-01 19:11:07 api_server.py:297] Route: /v1/completions, Methods: POST
INFO 08-01 19:11:07 api_server.py:297] Route: /v1/embeddings, Methods: POST
INFO:     Started server process [1]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
INFO:     Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)

vLLM API 서버 테스트

API 서버가 Llama 3.1 모델을 서빙하는지 테스트해요. Ctrl + B, 이어서 C로 새 tmux 창을 열고 아래 요청을 보내요.

curl -X POST http://localhost:8000/v1/completions \
     -H "Content-Type: application/json" \
     -d "{
           \"prompt\": \"What is the name of the capital of France?\",
           \"model\": \"${MODEL_REPO}\",
           \"temperature\": 0.0,
           \"max_tokens\": 1
         }"

다음과 비슷한 출력이 나와야 해요.

{"id":"cmpl-d3a33498b5d74d9ea09a7c256733b8df","object":"text_completion","created":1722545598,"model":"meta-llama/Meta-Llama-3.1-70B-Instruct","choices":[{"index":0,"text":" Paris","logprobs":null,"finish_reason":"length","stop_reason":null}],"usage":{"prompt_tokens":11,"total_tokens":12,"completion_tokens":1}}

출력이 한 줄로 뭉쳐 있어 읽기 어렵다면 jq를 쓰면 훨씬 보기 좋아져요. 먼저 jq를 설치해요.

sudo apt update && sudo apt install -y jq

그다음 앞의 curl 명령 끝에 | jq .를 붙이면 돼요.

curl -X POST http://localhost:8000/v1/completions \
     -H "Content-Type: application/json" \
     -d "{
           \"prompt\": \"What is the name of the capital of France?\",
           \"model\": \"${MODEL_REPO}\",
           \"temperature\": 0.0,
           \"max_tokens\": 1
         }" | jq .

출력이 이제 이렇게 예쁘게 정리돼요.

{
  "id": "cmpl-529d01c83069409fa5c166e1d137e21e",
  "object": "text_completion",
  "created": 1722545913,
  "model": "meta-llama/Meta-Llama-3.1-70B-Instruct",
  "choices": [
    {
      "index": 0,
      "text": " Paris",
      "logprobs": null,
      "finish_reason": "length",
      "stop_reason": null
    }
  ],
  "usage": {
    "prompt_tokens": 11,
    "total_tokens": 12,
    "completion_tokens": 1
  }
}

더 알아보기