Esegui il deployment di modelli aperti con un container vLLM personalizzato

Sebbene le varie opzioni di servizio dei modelli di Vertex AI siano sufficienti per molti casi d'uso, potresti dover utilizzare le tue immagini container per pubblicare i modelli su Vertex AI. Questo documento descrive come utilizzare un'immagine container personalizzata vLLM per pubblicare modelli su Vertex AI su CPU, GPU o TPU. Per saperne di più sui modelli supportati da vLLM, consulta la documentazione di vLLM.

Il server API vLLM implementa il protocollo API OpenAI, ma non supporta i requisiti di richiesta e risposta di Vertex AI. Pertanto, devi utilizzare una richiesta di inferenza non elaborata di Vertex AI per ottenere inferenze dai modelli di cui è stato eseguito il deployment su Vertex AI utilizzando un endpoint Prediction. Per saperne di più sul metodo Raw Prediction nell'SDK Vertex AI Python, consulta la documentazione dell'SDK Python.

Puoi utilizzare modelli di Hugging Face e Cloud Storage. Questo approccio offre flessibilità, consentendoti di sfruttare l'hub di modelli basato sulla community (Hugging Face) e le funzionalità di trasferimento e sicurezza dei dati ottimizzate di Cloud Storage per la gestione interna dei modelli o le versioni ottimizzate.

vLLM scarica i modelli da Hugging Face se viene fornito un token di accesso a Hugging Face. In caso contrario, vLLM presuppone che il modello sia disponibile sul disco locale. L'immagine container personalizzata consente a Vertex AI di scaricare il modello da Google Cloud oltre a Hugging Face.

Prima di iniziare

  1. Nel tuo progetto Google Cloud , abilita le API Vertex AI e Artifact Registry.

    gcloud services enable aiplatform.googleapis.com \
        artifactregistry.googleapis.com
    
  2. Configura Google Cloud CLI con l'ID progetto e inizializza l'SDK Vertex AI.

    PROJECT_ID = "PROJECT_ID"
    LOCATION = "LOCATION"
    import vertexai
    vertexai.init(project=PROJECT_ID, location=LOCATION)
    
    gcloud config set project {PROJECT_ID}
    
  3. Creare un repository Docker in Artifact Registry.

    gcloud artifacts repositories create DOCKER_REPOSITORY \
        --repository-format=docker \
        --location=LOCATION \
        --description="Vertex AI Docker repository"
    
  4. (Facoltativo) Se scarichi modelli da Hugging Face, ottieni un token Hugging Face.

    1. Crea un account Hugging Face se non ne hai uno.
    2. Per i modelli con accesso limitato come Llama 3.2, richiedi e ricevi l'accesso su Hugging Face prima di procedere.
    3. Genera un token di accesso: vai a Il tuo profilo > Impostazioni > Token di accesso.
    4. Seleziona Nuovo token.
    5. Specifica un nome e un ruolo di almeno Lettura.
    6. Seleziona Genera un token.
    7. Salva questo token per i passaggi di deployment.

Prepara i file di compilazione del container

Il seguente Dockerfile crea l'immagine container personalizzata vLLM per GPU, TPU e CPU. Questo container personalizzato scarica i modelli da Hugging Face o Cloud Storage.

ARG BASE_IMAGE
FROM ${BASE_IMAGE}

ENV DEBIAN_FRONTEND=noninteractive
# Install gcloud SDK
RUN apt-get update && \
    apt-get install -y apt-utils git apt-transport-https gnupg ca-certificates curl \
    && echo "deb [signed-by=/usr/share/keyrings/cloud.google.gpg] https://packages.cloud.google.com/apt cloud-sdk main" | tee -a /etc/apt/sources.list.d/google-cloud-sdk.list \
    && curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | gpg --dearmor -o /usr/share/keyrings/cloud.google.gpg \
    && apt-get update -y && apt-get install google-cloud-cli -y \
    && rm -rf /var/lib/apt/lists/*

WORKDIR /workspace/vllm

# Copy entrypoint.sh to the container
COPY ./entrypoint.sh /workspace/vllm/vertexai/entrypoint.sh
RUN chmod +x /workspace/vllm/vertexai/entrypoint.sh

ENTRYPOINT ["/workspace/vllm/vertexai/entrypoint.sh"]

Crea l'immagine container personalizzata utilizzando Cloud Build. Il seguente file di configurazione cloudbuild.yaml mostra come creare l'immagine per più piattaforme utilizzando lo stesso Dockerfile.

steps:
-   name: 'gcr.io/cloud-builders/docker'
  automapSubstitutions: true
  script: |
      #!/usr/bin/env bash
      set -euo pipefail
      device_type_param=${_DEVICE_TYPE}
      device_type=${device_type_param,,}
      base_image=${_BASE_IMAGE}
      image_name="vllm-${_DEVICE_TYPE}"
      if [[ $device_type == "cpu" ]]; then
        echo "Quietly building open source vLLM CPU container image"
        git clone https://github.com/vllm-project/vllm.git
        cd vllm && DOCKER_BUILDKIT=1 docker build -t $base_image -f docker/Dockerfile.cpu . -q
        cd ..
      fi
      echo "Quietly building container image for: $device_type"
      docker build -t $LOCATION-docker.pkg.dev/$PROJECT_ID/${_REPOSITORY}/$image_name --build-arg BASE_IMAGE=$base_image . -q
      docker push $LOCATION-docker.pkg.dev/$PROJECT_ID/${_REPOSITORY}/$image_name
substitutions:
    _DEVICE_TYPE: gpu
    _BASE_IMAGE: vllm/vllm-openai
    _REPOSITORY: my-docker-repo

I file sono disponibili nel repository GitHub googlecloudplatform/vertex-ai-samples. Clona il repository per utilizzarli:

git clone https://github.com/GoogleCloudPlatform/vertex-ai-samples.git

Crea ed esegui il push dell'immagine container

Crea l'immagine container personalizzata utilizzando Cloud Build inviando il file cloudbuild.yaml. Utilizza le sostituzioni per specificare il tipo di dispositivo di destinazione, che può essere GPU, TPU o CPU, e l'immagine di base corrispondente.

GPU

DEVICE_TYPE="gpu"
BASE_IMAGE="vllm/vllm-openai"
cd vertex-ai-samples/notebooks/official/prediction/vertexai_serving_vllm/cloud-build && \
gcloud builds submit \
    --config=cloudbuild.yaml \
    --region=LOCATION \
    --timeout="2h" \
    --machine-type=e2-highcpu-32 \
    --substitutions=_REPOSITORY=DOCKER_REPOSITORY,_DEVICE_TYPE=$DEVICE_TYPE,_BASE_IMAGE=$BASE_IMAGE

TPU

DEVICE_TYPE="tpu"
BASE_IMAGE="vllm/vllm-tpu:nightly"
cd vertex-ai-samples/notebooks/official/prediction/vertexai_serving_vllm/cloud-build && \
gcloud builds submit \
    --config=cloudbuild.yaml \
    --region=LOCATION \
    --timeout="2h" \
    --machine-type=e2-highcpu-32 \
    --substitutions=_REPOSITORY=DOCKER_REPOSITORY,_DEVICE_TYPE=$DEVICE_TYPE,_BASE_IMAGE=$BASE_IMAGE

CPU

DEVICE_TYPE="cpu"
BASE_IMAGE="vllm-cpu-base"
cd vertex-ai-samples/notebooks/official/prediction/vertexai_serving_vllm/cloud-build && \
gcloud builds submit \
    --config=cloudbuild.yaml \
    --region=LOCATION \
    --timeout="2h" \
    --machine-type=e2-highcpu-32 \
    --substitutions=_REPOSITORY=DOCKER_REPOSITORY,_DEVICE_TYPE=$DEVICE_TYPE,_BASE_IMAGE=$BASE_IMAGE

Al termine della build, configura Docker per l'autenticazione con Artifact Registry:

gcloud auth configure-docker LOCATION-docker.pkg.dev --quiet

Carica il modello in Model Registry ed esegui il deployment

Carica il modello in Vertex AI Model Registry, crea un endpoint ed esegui il deployment del modello completando questi passaggi. Questo esempio utilizza Llama 3.2 3B, ma puoi adattarlo ad altri modelli.

  1. Definisci le variabili del modello e del deployment. Imposta la variabile DOCKER_URI sull'immagine che hai creato nel passaggio precedente (ad esempio, per la GPU):

    DOCKER_URI = f"LOCATION-docker.pkg.dev/PROJECT_ID/DOCKER_REPOSITORY/vllm-gpu"
    

    Definisci le variabili per il token Hugging Face e le proprietà del modello. Ad esempio, per il deployment della GPU:

    hf_token = "your-hugging-face-auth-token"
    model_name = "gpu-llama3_2_3B-serve-vllm"
    model_id = "meta-llama/Llama-3.2-3B"
    machine_type = "g2-standard-8"
    accelerator_type = "NVIDIA_L4"
    accelerator_count = 1
    
  2. Carica il modello in Model Registry. La funzione upload_model varia leggermente a seconda del tipo di dispositivo a causa di diversi argomenti vLLM e variabili di ambiente.

    from google.cloud import aiplatform
    
    def upload_model_gpu(model_name, model_id, hf_token, accelerator_count, docker_uri):
        vllm_args = [
            "python3", "-m", "vllm.entrypoints.openai.api_server",
            "--host=0.0.0.0", "--port=8080", f"--model={model_id}",
            "--max-model-len=2048", "--gpu-memory-utilization=0.9",
            "--enable-prefix-caching", f"--tensor-parallel-size={accelerator_count}",
        ]
        env_vars = {
            "HF_TOKEN": hf_token,
            "LD_LIBRARY_PATH": "$LD_LIBRARY_PATH:/usr/local/nvidia/lib64",
        }
        model = aiplatform.Model.upload(
            display_name=model_name,
            serving_container_image_uri=docker_uri,
            serving_container_args=vllm_args,
            serving_container_ports=[8080],
            serving_container_predict_route="/v1/completions",
            serving_container_health_route="/health",
            serving_container_environment_variables=env_vars,
            serving_container_shared_memory_size_mb=(16 * 1024),  # 16 GB
            serving_container_deployment_timeout=1800,
        )
        return model
    
    def upload_model_tpu(model_name, model_id, hf_token, tpu_count, docker_uri):
        vllm_args = [
            "python3", "-m", "vllm.entrypoints.openai.api_server",
            "--host=0.0.0.0", "--port=8080", f"--model={model_id}",
            "--max-model-len=2048", "--enable-prefix-caching",
            f"--tensor-parallel-size={tpu_count}",
        ]
        env_vars = {"HF_TOKEN": hf_token}
        model = aiplatform.Model.upload(
            display_name=model_name,
            serving_container_image_uri=docker_uri,
            serving_container_args=vllm_args,
            serving_container_ports=[8080],
            serving_container_predict_route="/v1/completions",
            serving_container_health_route="/health",
            serving_container_environment_variables=env_vars,
            serving_container_shared_memory_size_mb=(16 * 1024),  # 16 GB
            serving_container_deployment_timeout=1800,
        )
        return model
    
    def upload_model_cpu(model_name, model_id, hf_token, docker_uri):
        vllm_args = [
            "python3", "-m", "vllm.entrypoints.openai.api_server",
            "--host=0.0.0.0", "--port=8080", f"--model={model_id}",
            "--max-model-len=2048",
        ]
        env_vars = {"HF_TOKEN": hf_token}
        model = aiplatform.Model.upload(
            display_name=model_name,
            serving_container_image_uri=docker_uri,
            serving_container_args=vllm_args,
            serving_container_ports=[8080],
            serving_container_predict_route="/v1/completions",
            serving_container_health_route="/health",
            serving_container_environment_variables=env_vars,
            serving_container_shared_memory_size_mb=(16 * 1024),  # 16 GB
            serving_container_deployment_timeout=1800,
        )
        return model
    
    # Example for GPU:
    vertexai_model = upload_model_gpu(model_name, model_id, hf_token, accelerator_count, DOCKER_URI)
    
  3. Crea un endpoint.

    endpoint = aiplatform.Endpoint.create(display_name=f"model_name-endpoint")
    
  4. Esegui il deployment del modello sull'endpoint. Il deployment del modello potrebbe richiedere 20-30 minuti.

    # Example for GPU:
    vertexai_model.deploy(
        endpoint=endpoint,
        deployed_model_display_name=model_name,
        machine_type=machine_type,
        accelerator_type=accelerator_type,
        accelerator_count=accelerator_count,
        traffic_percentage=100,
        deploy_request_timeout=1800,
        min_replica_count=1,
        max_replica_count=4,
        autoscaling_target_accelerator_duty_cycle=60,
    )
    

    Per le TPU, ometti i parametri accelerator_type e accelerator_count e utilizza autoscaling_target_request_count_per_minute=60. Per le CPU, ometti i parametri accelerator_type e accelerator_count e utilizza autoscaling_target_cpu_utilization=60.

Carica modelli da Cloud Storage

Il container personalizzato scarica il modello da una posizione Cloud Storage anziché da Hugging Face. Quando utilizzi Cloud Storage:

  • Imposta il parametro model_id nella funzione upload_model su un URI Cloud Storage, ad esempio gs://<var>my-bucket</var>/<var>my-models</var>/<var>llama_3_2_3B</var>.
  • Ometti la variabile HF_TOKEN da env_vars quando chiami upload_model.
  • Quando chiami model.deploy, specifica un service_account che disponga delle autorizzazioni di lettura dal bucket Cloud Storage.

Crea un service account IAM per l'accesso a Cloud Storage

Se il modello si trova in Cloud Storage, crea un account di servizio che gli endpoint Vertex Prediction possono utilizzare per accedere agli artefatti del modello.

SERVICE_ACCOUNT_NAME = "vertexai-endpoint-sa"
SERVICE_ACCOUNT_EMAIL = f"SERVICE_ACCOUNT_NAME@PROJECT_ID.iam.gserviceaccount.com"
gcloud iam service-accounts create SERVICE_ACCOUNT_NAME \
    --display-name="Vertex AI Endpoint Service Account"

# Grant storage read permission
gcloud projects add-iam-policy-binding PROJECT_ID \
    --member="serviceAccount:SERVICE_ACCOUNT_EMAIL" \
    --role="roles/storage.objectViewer"

Quando esegui il deployment, passa l'email del account di servizio al metodo deploy: service_account=<var>SERVICE_ACCOUNT_EMAIL</var>.

Ottenere previsioni utilizzando l'endpoint

Dopo aver eseguito il deployment del modello nell'endpoint, verifica la risposta del modello utilizzando raw_predict.

import json

PROMPT = "Distance of moon from earth is "
request_body = json.dumps(
    {
        "prompt": PROMPT,
        "temperature": 0.0,
    },
)

raw_response = endpoint.raw_predict(
    body=request_body, headers={"Content-Type": "application/json"}
)
assert raw_response.status_code == 200
result = json.loads(raw_response.text)

for choice in result["choices"]:
    print(choice)

Output di esempio:

{
  "index": 0,
  "text": "384,400 km. The moon is 1/4 of the earth's",
  "logprobs": null,
  "finish_reason": "length",
  "stop_reason": null,
  "prompt_logprobs": null
}

Passaggi successivi