Esegui il deployment di modelli aperti con un container vLLM personalizzato

Sebbene le varie opzioni di erogazione del modello di Gemini Enterprise Agent Platform siano sufficienti per molti casi d'uso, potresti dover utilizzare le tue immagini container per erogare modelli su Gemini Enterprise Agent Platform. Questo documento descrive come utilizzare un'immagine container personalizzata vLLM per gestire i modelli su Gemini Enterprise Agent Platform su CPU, GPU o TPU. Per ulteriori informazioni sui modelli supportati da vLLM, consulta la vLLM documentazione.

Il server API vLLM implementa il protocollo API OpenAI, ma non supporta i requisiti di richiesta e risposta di Gemini Enterprise Agent Platform. Pertanto, devi utilizzare una richiesta di inferenza non elaborata di Gemini Enterprise Agent Platform per ottenere inferenze dai modelli di cui è stato eseguito il deployment su Gemini Enterprise Agent Platform utilizzando un endpoint di previsione. Per ulteriori informazioni sul metodo di previsione non elaborata nell'SDK Python di Gemini Enterprise Agent Platform, consulta la documentazione dell'SDK Python.

Puoi recuperare i modelli da Hugging Face e Cloud Storage. Questo approccio offre flessibilità, consentendoti di sfruttare l'hub di modelli basato sulla community (Hugging Face) e le funzionalità ottimizzate di trasferimento e sicurezza dei dati 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 Gemini Enterprise Agent Platform di scaricare il modello da Google Cloud in aggiunta a Hugging Face.

Prima di iniziare

  1. Nel tuo Google Cloud progetto, attiva le API Gemini Enterprise Agent Platform 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. Crea un repository Docker in Artifact Registry.

    gcloud artifacts repositories create DOCKER_REPOSITORY \
        --repository-format=docker \
        --location=LOCATION \
        --description="Agent Platform Docker repository"
    
  4. (Facoltativo) Se scarichi i modelli da Hugging Face, ottieni un token di 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.

Preparare i file di creazione 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

Creare ed eseguire 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 creazione, configura Docker per l'autenticazione con Artifact Registry:

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

Caricare il modello in Model Registry ed eseguire il deployment

Carica il modello in 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 di 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 e variabili di ambiente vLLM.

    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.

Caricare i modelli da Cloud Storage

Il container personalizzato scarica il modello da una località di 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 per leggere dal bucket Cloud Storage.

Creare un account di servizio IAM per l'accesso a Cloud Storage

Se il modello si trova in Cloud Storage, crea un account di servizio che gli endpoint di previsione di Vertex 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="Agent Platform 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'indirizzo email del account di servizio al deploy metodo: service_account=<var>SERVICE_ACCOUNT_EMAIL</var>.

Ottenere previsioni utilizzando l'endpoint

Dopo aver eseguito correttamente il deployment del modello sull'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