วิธีปรับแต่ง LLM โดยใช้งาน Cloud Run

วิธีปรับแต่ง LLM โดยใช้งาน Cloud Run

เกี่ยวกับ Codelab นี้

subjectอัปเดตล่าสุดเมื่อ มิ.ย. 3, 2025
account_circleเขียนโดย Googler

1 บทนำ

ในโค้ดแล็บนี้ คุณจะใช้งาน Cloud Run เพื่อปรับแต่งโมเดล Gemma จากนั้นแสดงผลลัพธ์ใน Cloud Run โดยใช้ vLLM

สําหรับวัตถุประสงค์ของโค้ดแล็บนี้ คุณจะใช้ชุดข้อมูลการแปลงข้อความเป็น SQL โดยมีจุดประสงค์เพื่อทําให้ LLM ตอบกลับด้วยการค้นหา SQL เมื่อได้รับคําถามที่เป็นภาษาธรรมชาติ

  • วิธีปรับแต่งโดยใช้ GPU ของ Cloud Run Jobs
  • วิธีแสดงโมเดลโดยใช้ Cloud Run ที่มี vLLM
  • วิธีใช้การกำหนดค่า VPC โดยตรงสำหรับงาน GPU เพื่ออัปโหลดและแสดงโมเดลได้เร็วขึ้น

2 ก่อนเริ่มต้น

เปิดใช้ API

คุณต้องเปิดใช้ API ต่อไปนี้โดยเรียกใช้คำสั่งต่อไปนี้ก่อนจึงจะเริ่มใช้ Codelab นี้ได้

gcloud services enable run.googleapis.com \
    compute.googleapis.com \
    run.googleapis.com \
    cloudbuild.googleapis.com \
    secretmanager.googleapis.com \
    artifactregistry.googleapis.com

โควต้า GPU

ขอเพิ่มโควต้าสำหรับภูมิภาคที่รองรับ โควต้าคือ nvidia_l4_gpu_allocation_no_zonal_redundancy ใน Cloud Run Admin API

หมายเหตุ: หากคุณใช้โปรเจ็กต์ใหม่ ระบบอาจใช้เวลา 2-3 นาทีระหว่างการเปิดใช้ API กับการแสดงโควต้าในหน้านี้

หน้ากอด

Codelab นี้ใช้โมเดลที่โฮสต์ใน Hugging Face หากต้องการใช้โมเดลนี้ ให้ขอโทเค็นการเข้าถึงผู้ใช้ Hugging Face ที่มีสิทธิ์ "อ่าน" คุณจะอ้างอิงถึงรายการนี้ในภายหลังโดยใช้ชื่อ YOUR_HF_TOKEN

นอกจากนี้ คุณจะต้องยอมรับข้อกำหนดในการใช้งานเพื่อใช้โมเดล https://huggingface.co/google/gemma-2b

3 การตั้งค่าและข้อกําหนด

ตั้งค่าทรัพยากรต่อไปนี้

  • บัญชีบริการ IAM และสิทธิ์ IAM ที่เชื่อมโยง
  • ข้อมูลลับของ Secret Manager เพื่อจัดเก็บโทเค็น Hugging Face
  • ที่เก็บข้อมูล Cloud Storage เพื่อจัดเก็บโมเดลที่ปรับแต่ง และ
  • ที่เก็บ Artifact Registry เพื่อจัดเก็บรูปภาพที่คุณจะใช้สร้างเพื่อปรับแต่งโมเดล
  1. ตั้งค่าตัวแปรสภาพแวดล้อมสําหรับ Codelab นี้ เราได้ป้อนข้อมูลตัวแปรจํานวนหนึ่งไว้ล่วงหน้าให้คุณแล้ว ระบุรหัสโปรเจ็กต์ ภูมิภาค และโทเค็น Hugging Face
    export PROJECT_ID=<YOUR_PROJECT_ID>
    export REGION=<YOUR_REGION>
    export HF_TOKEN=<YOUR_HF_TOKEN>

    export AR_REPO=codelab-finetuning-jobs
    export IMAGE_NAME=finetune-to-gcs
    export JOB_NAME=finetuning-to-gcs-job
    export BUCKET_NAME=$PROJECT_ID-codelab-finetuning-jobs
    export SECRET_ID=HF_TOKEN
    export SERVICE_ACCOUNT="finetune-job-sa"
    export SERVICE_ACCOUNT_ADDRESS=$SERVICE_ACCOUNT@$PROJECT_ID.iam.gserviceaccount.com
  2. สร้างบัญชีบริการโดยเรียกใช้คําสั่งนี้
    gcloud iam service-accounts create $SERVICE_ACCOUNT \
     
    --display-name="Service account for fine-tuning codelab"
  3. ใช้ Secret Manager เพื่อจัดเก็บโทเค็นการเข้าถึง Hugging Face โดยทำดังนี้
    gcloud secrets create $SECRET_ID \
         
    --replication-policy="automatic"

    printf $HF_TOKEN
    | gcloud secrets versions add $SECRET_ID --data-file=-
  4. มอบบทบาทผู้เข้าถึงข้อมูลลับของผู้จัดการข้อมูลลับให้กับบัญชีบริการ
    gcloud secrets add-iam-policy-binding $SECRET_ID \
     
    --member serviceAccount:$SERVICE_ACCOUNT_ADDRESS \
     
    --role='roles/secretmanager.secretAccessor'
  5. สร้างที่เก็บข้อมูลที่จะโฮสต์โมเดลที่ปรับแต่งแล้ว โดยทำดังนี้
    gcloud storage buckets create -l $REGION gs://$BUCKET_NAME
  6. ให้สิทธิ์เข้าถึงที่เก็บข้อมูลแก่บัญชีบริการโดยทำดังนี้
    gcloud storage buckets add-iam-policy-binding gs://$BUCKET_NAME \
     
    --member=serviceAccount:$SERVICE_ACCOUNT_ADDRESS \
     
    --role=roles/storage.objectAdmin
  7. สร้างที่เก็บ Artifact Registry เพื่อจัดเก็บอิมเมจคอนเทนเนอร์ โดยทำดังนี้
    gcloud artifacts repositories create $AR_REPO \
       
    --repository-format=docker \
       
    --location=$REGION \
       
    --description="codelab for finetuning using CR jobs" \
       
    --project=$PROJECT_ID

4 สร้างรูปภาพงาน Cloud Run

ในขั้นตอนถัดไป คุณจะต้องสร้างโค้ดที่ทําสิ่งต่อไปนี้

  • นําเข้าโมเดล Gemma จาก Hugging Face
  • ปรับแต่งโมเดลด้วยชุดข้อมูลจาก Hugging Face งานใช้ GPU L4 รายการเดียวเพื่อปรับแต่ง
  • อัปโหลดโมเดลที่ปรับแต่งอย่างละเอียดชื่อ new_model ไปยังที่เก็บข้อมูล Cloud Storage
  1. สร้างไดเรกทอรีสำหรับรหัสงานการปรับแต่ง
    mkdir codelab-finetuning-job
    cd codelab
    -finetuning-job
  2. สร้างไฟล์ชื่อ finetune.py
    # Copyright 2024 Google LLC
    #
    # Licensed under the Apache License, Version 2.0 (the "License");
    # you may not use this file except in compliance with the License.
    # You may obtain a copy of the License at
    #
    #      http://www.apache.org/licenses/LICENSE-2.0
    #
    # Unless required by applicable law or agreed to in writing, software
    # distributed under the License is distributed on an "AS IS" BASIS,
    # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    # See the License for the specific language governing permissions and
    # limitations under the License.

    import os
    import torch
    from datasets import load_dataset
    from transformers import (
       
    AutoModelForCausalLM,
       
    AutoTokenizer,
       
    BitsAndBytesConfig,
       
    TrainingArguments,

    )
    from peft import LoraConfig, PeftModel

    from trl import SFTTrainer

    # Cloud Storage bucket to upload the model
    bucket_name = os.getenv("BUCKET_NAME", "YOUR_BUCKET_NAME")

    # The model that you want to train from the Hugging Face hub
    model_name = os.getenv("MODEL_NAME", "google/gemma-2b")

    # The instruction dataset to use
    dataset_name = "b-mc2/sql-create-context"

    # Fine-tuned model name
    new_model = os.getenv("NEW_MODEL", "gemma-2b-sql")

    ################################################################################
    # QLoRA parameters
    ################################################################################

    # LoRA attention dimension
    lora_r = int(os.getenv("LORA_R", "4"))

    # Alpha parameter for LoRA scaling
    lora_alpha = int(os.getenv("LORA_ALPHA", "8"))

    # Dropout probability for LoRA layers
    lora_dropout = 0.1

    ################################################################################
    # bitsandbytes parameters
    ################################################################################

    # Activate 4-bit precision base model loading
    use_4bit = True

    # Compute dtype for 4-bit base models
    bnb_4bit_compute_dtype = "float16"

    # Quantization type (fp4 or nf4)
    bnb_4bit_quant_type = "nf4"

    # Activate nested quantization for 4-bit base models (double quantization)
    use_nested_quant = False

    ################################################################################
    # TrainingArguments parameters
    ################################################################################

    # Output directory where the model predictions and checkpoints will be stored
    output_dir = "./results"

    # Number of training epochs
    num_train_epochs = 1

    # Enable fp16/bf16 training (set bf16 to True with an A100)
    fp16 = True
    bf16 = False

    # Batch size per GPU for training
    per_device_train_batch_size = int(os.getenv("TRAIN_BATCH_SIZE", "1"))

    # Batch size per GPU for evaluation
    per_device_eval_batch_size = int(os.getenv("EVAL_BATCH_SIZE", "2"))

    # Number of update steps to accumulate the gradients for
    gradient_accumulation_steps = int(os.getenv("GRADIENT_ACCUMULATION_STEPS", "1"))

    # Enable gradient checkpointing
    gradient_checkpointing = True

    # Maximum gradient normal (gradient clipping)
    max_grad_norm = 0.3

    # Initial learning rate (AdamW optimizer)
    learning_rate = 2e-4

    # Weight decay to apply to all layers except bias/LayerNorm weights
    weight_decay = 0.001

    # Optimizer to use
    optim = "paged_adamw_32bit"

    # Learning rate schedule
    lr_scheduler_type = "cosine"

    # Number of training steps (overrides num_train_epochs)
    max_steps = -1

    # Ratio of steps for a linear warmup (from 0 to learning rate)
    warmup_ratio = 0.03

    # Group sequences into batches with same length
    # Saves memory and speeds up training considerably
    group_by_length = True

    # Save checkpoint every X updates steps
    save_steps = 0

    # Log every X updates steps
    logging_steps = int(os.getenv("LOGGING_STEPS", "50"))

    ################################################################################
    # SFT parameters
    ################################################################################

    # Maximum sequence length to use
    max_seq_length = int(os.getenv("MAX_SEQ_LENGTH", "512"))

    # Pack multiple short examples in the same input sequence to increase efficiency
    packing = False

    # Load the entire model on the GPU 0
    device_map = {'':torch.cuda.current_device()}

    # Set limit to a positive number
    limit = int(os.getenv("DATASET_LIMIT", "5000"))

    dataset = load_dataset(dataset_name, split="train")
    if limit != -1:
       
    dataset = dataset.shuffle(seed=42).select(range(limit))


    def transform(data):
       
    question = data['question']
       
    context = data['context']
       
    answer = data['answer']
       
    template = "Question: {question}\nContext: {context}\nAnswer: {answer}"
       
    return {'text': template.format(question=question, context=context, answer=answer)}


    transformed = dataset.map(transform)

    # Load tokenizer and model with QLoRA configuration
    compute_dtype = getattr(torch, bnb_4bit_compute_dtype)

    bnb_config = BitsAndBytesConfig(
       
    load_in_4bit=use_4bit,
       
    bnb_4bit_quant_type=bnb_4bit_quant_type,
       
    bnb_4bit_compute_dtype=compute_dtype,
       
    bnb_4bit_use_double_quant=use_nested_quant,
    )

    # Check GPU compatibility with bfloat16
    if compute_dtype == torch.float16 and use_4bit:
       
    major, _ = torch.cuda.get_device_capability()
       
    if major >= 8:
           
    print("=" * 80)
           
    print("Your GPU supports bfloat16")
           
    print("=" * 80)

    # Load base model
    model = AutoModelForCausalLM.from_pretrained(
       
    model_name,
       
    quantization_config=bnb_config,
       
    device_map=device_map,
       
    torch_dtype=torch.float16,
    )
    model.config.use_cache = False
    model.config.pretraining_tp = 1

    # Load LLaMA tokenizer
    tokenizer = AutoTokenizer.from_pretrained(model_name, trust_remote_code=True)
    tokenizer.pad_token = tokenizer.eos_token
    tokenizer.padding_side = "right"

    # Load LoRA configuration
    peft_config = LoraConfig(
       
    lora_alpha=lora_alpha,
       
    lora_dropout=lora_dropout,
       
    r=lora_r,
       
    bias="none",
       
    task_type="CAUSAL_LM",
       
    target_modules=["q_proj", "v_proj"]
    )

    # Set training parameters
    training_arguments = TrainingArguments(
       
    output_dir=output_dir,
       
    num_train_epochs=num_train_epochs,
       
    per_device_train_batch_size=per_device_train_batch_size,
       
    gradient_accumulation_steps=gradient_accumulation_steps,
       
    optim=optim,
       
    save_steps=save_steps,
       
    logging_steps=logging_steps,
       
    learning_rate=learning_rate,
       
    weight_decay=weight_decay,
       
    fp16=fp16,
       
    bf16=bf16,
       
    max_grad_norm=max_grad_norm,
       
    max_steps=max_steps,
       
    warmup_ratio=warmup_ratio,
       
    group_by_length=group_by_length,
       
    lr_scheduler_type=lr_scheduler_type,
    )

    trainer = SFTTrainer(
       
    model=model,
       
    train_dataset=transformed,
       
    peft_config=peft_config,
       
    dataset_text_field="text",
       
    max_seq_length=max_seq_length,
       
    tokenizer=tokenizer,
       
    args=training_arguments,
       
    packing=packing,
    )

    trainer.train()

    trainer.model.save_pretrained(new_model)

    # Reload model in FP16 and merge it with LoRA weights
    base_model = AutoModelForCausalLM.from_pretrained(
       
    model_name,
       
    low_cpu_mem_usage=True,
       
    return_dict=True,
       
    torch_dtype=torch.float16,
       
    device_map=device_map,
    )
    model = PeftModel.from_pretrained(base_model, new_model)
    model = model.merge_and_unload()

    # push to Cloud Storage

    file_path_to_save_the_model = '/finetune/new_model'
    model.save_pretrained(file_path_to_save_the_model)
    tokenizer.save_pretrained(file_path_to_save_the_model)
  3. วิธีสร้างไฟล์ requirements.txt
    accelerate==0.34.2
    bitsandbytes
    ==0.45.5
    datasets
    ==2.19.1
    transformers
    ==4.51.3
    peft
    ==0.11.1
    trl
    ==0.8.6
    torch
    ==2.3.0
  4. วิธีสร้าง Dockerfile
    FROM nvidia/cuda:12.6.2-runtime-ubuntu22.04

    RUN apt-get update && \
        apt-get -y --no-install-recommends install python3-dev gcc python3-pip git && \
        rm -rf /var/lib/apt/lists/*

    COPY requirements.txt /requirements.txt

    RUN pip3 install -r requirements.txt --no-cache-dir

    COPY finetune.py /finetune.py

    ENV PYTHONUNBUFFERED 1

    CMD python3 /finetune.py --device cuda
  5. บิลด์คอนเทนเนอร์ในที่เก็บ Artifact Registry โดยทำดังนี้
    gcloud builds submit \
      --tag $REGION-docker.pkg.dev/$PROJECT_ID/$AR_REPO/$IMAGE_NAME \
      --region $REGION

5 ติดตั้งใช้งานและเรียกใช้งาน

ในขั้นตอนนี้ คุณจะต้องสร้างการกําหนดค่า YAML สําหรับงานของคุณด้วยการออก VPC โดยตรงเพื่อให้อัปโหลดไปยัง Google Cloud Storage ได้เร็วขึ้น

โปรดทราบว่าไฟล์นี้มีตัวแปรที่คุณจะอัปเดตในขั้นตอนถัดไป

  1. สร้างไฟล์ชื่อ finetune-job.yaml.tmpl โดยทำดังนี้
    apiVersion: run.googleapis.com/v1
    kind: Job
    metadata:
      name: $JOB_NAME
      labels:
        cloud.googleapis.com/location: $REGION
      annotations:
        run.googleapis.com/launch-stage: ALPHA
    spec:
      template:
        metadata:
          annotations:
            run.googleapis.com/execution-environment: gen2
            run.googleapis.com/network-interfaces: '[{"network":"default","subnetwork":"default"}]'
        spec:
          parallelism: 1
          taskCount: 1
          template:
            spec:
              serviceAccountName: $SERVICE_ACCOUNT_ADDRESS
              containers:
              - name: $IMAGE_NAME
                image: $REGION-docker.pkg.dev/$PROJECT_ID/$AR_REPO/$IMAGE_NAME
                env:
                - name: MODEL_NAME
                  value: "google/gemma-2b"
                - name: NEW_MODEL
                  value: "gemma-2b-sql-finetuned"
                - name: BUCKET_NAME
                  value: "$BUCKET_NAME"
                - name: LORA_R
                  value: "8"
                - name: LORA_ALPHA
                  value: "16"
                - name: GRADIENT_ACCUMULATION_STEPS
                  value: "2"
                - name: DATASET_LIMIT
                  value: "1000"
                - name: LOGGING_STEPS
                  value: "5"
                - name: HF_TOKEN
                  valueFrom:
                    secretKeyRef:
                      key: 'latest'
                      name: HF_TOKEN
                resources:
                  limits:
                    cpu: 8000m
                    nvidia.com/gpu: '1'
                    memory: 32Gi
                volumeMounts:
                - mountPath: /finetune/new_model
                  name: finetuned_model
              volumes:
              - name: finetuned_model
                csi:
                  driver: gcsfuse.run.googleapis.com
                  readOnly: false
                  volumeAttributes:
                    bucketName: $BUCKET_NAME
              maxRetries: 3
              timeoutSeconds: '3600'
              nodeSelector:
                run.googleapis.com/accelerator: nvidia-l4
  2. แทนที่ตัวแปรใน YAML ด้วยตัวแปรสภาพแวดล้อมโดยเรียกใช้คําสั่งต่อไปนี้
    envsubst < finetune-job.yaml.tmpl > finetune-job.yaml
  3. สร้างงาน Cloud Run โดยทำดังนี้
    gcloud alpha run jobs replace finetune-job.yaml
  4. เรียกใช้งาน
    gcloud alpha run jobs execute $JOB_NAME --region $REGION --async

ซึ่งจะใช้เวลาประมาณ 10 นาที คุณสามารถตรวจสอบสถานะได้โดยใช้ลิงก์ที่ให้ไว้ในเอาต์พุตของคำสั่งสุดท้าย

6 ใช้บริการ Cloud Run เพื่อแสดงโมเดลที่ปรับแต่งด้วย vLLM

ในขั้นตอนนี้ คุณจะทําให้บริการ Cloud Run ใช้งานได้ การกําหนดค่านี้ใช้ VPC โดยตรงเพื่อเข้าถึงที่เก็บข้อมูล Cloud Storage ผ่านเครือข่ายส่วนตัวเพื่อการดาวน์โหลดที่เร็วขึ้น

โปรดทราบว่าไฟล์นี้มีตัวแปรที่คุณจะอัปเดตในขั้นตอนถัดไป

  1. วิธีสร้างไฟล์ service.yaml.tmpl
    apiVersion: serving.knative.dev/v1
    kind: Service
    metadata:
      name: serve-gemma-sql
      labels:
        cloud.googleapis.com/location: $REGION
      annotations:
        run.googleapis.com/launch-stage: BETA
        run.googleapis.com/ingress: all
        run.googleapis.com/ingress-status: all
    spec:
      template:
        metadata:
          labels:
          annotations:
            autoscaling.knative.dev/maxScale: '1'
            run.googleapis.com/cpu-throttling: 'false'
            run.googleapis.com/gpu-zonal-redundancy-disabled: 'true'
            run.googleapis.com/network-interfaces: '[{"network":"default","subnetwork":"default"}]'
        spec:
          containers:
          - name: serve-finetuned
            image: us-docker.pkg.dev/vertex-ai/vertex-vision-model-garden-dockers/pytorch-vllm-serve:20250505_0916_RC00
            ports:
            - name: http1
              containerPort: 8000
            resources:
              limits:
                cpu: 8000m
                nvidia.com/gpu: '1'
                memory: 32Gi
            volumeMounts:
            - name: fuse
              mountPath: /finetune/new_model
            command: ["python3", "-m", "vllm.entrypoints.api_server"]
            args:
            - --model=/finetune/new_model
            - --tensor-parallel-size=1
            env:
            - name: MODEL_ID
              value: 'new_model'
            - name: HF_HUB_OFFLINE
              value: '1'
          volumes:
          - name: fuse
            csi:
              driver: gcsfuse.run.googleapis.com
              volumeAttributes:
                bucketName: $BUCKET_NAME
          nodeSelector:
            run.googleapis.com/accelerator: nvidia-l4
  2. อัปเดตไฟล์ service.yaml ด้วยชื่อที่เก็บข้อมูล
    envsubst < service.yaml.tmpl > service.yaml
  3. เผยแพร่บริการ Cloud Run โดยทำดังนี้
    gcloud alpha run services replace service.yaml

7 ทดสอบโมเดลที่ปรับแต่งแล้ว

ในขั้นตอนนี้ คุณจะบอกให้โมเดลทดสอบการปรับแต่ง

  1. รับ URL บริการสำหรับบริการ Cloud Run โดยทำดังนี้
    SERVICE_URL=$(gcloud run services describe serve-gemma-sql --platform managed --region $REGION --format 'value(status.url)')
  2. สร้างพรอมต์สำหรับโมเดล
    USER_PROMPT="Question: What are the first name and last name of all candidates? Context: CREATE TABLE candidates (candidate_id VARCHAR); CREATE TABLE people (first_name VARCHAR, last_name VARCHAR, person_id VARCHAR)"
  3. เรียกใช้บริการโดยใช้ CURL เพื่อแสดงพรอมต์โมเดล
    curl -X POST $SERVICE_URL/generate \
      -H "Content-Type: application/json" \
      -H "Authorization: bearer $(gcloud auth print-identity-token)" \
      -d @- <<EOF
    {
        "prompt": "${USER_PROMPT}"
    }
    EOF

คุณควรเห็นการตอบกลับที่คล้ายกับตัวอย่างต่อไปนี้

{"predictions":["Prompt:\nQuestion: What are the first name and last name of all candidates? Context: CREATE TABLE candidates (candidate_id VARCHAR); CREATE TABLE people (first_name VARCHAR, last_name VARCHAR, person_id VARCHAR)\nOutput:\n CREATE TABLE people_to_candidates (candidate_id VARCHAR, person_id VARCHAR) CREATE TABLE people_to_people (person_id VARCHAR, person_id VARCHAR) CREATE TABLE people_to_people_to_candidates (person_id VARCHAR, candidate_id"]}

8 ยินดีด้วย

ยินดีด้วยที่ทํา Codelab จนเสร็จสมบูรณ์

เราขอแนะนําให้อ่านเอกสารประกอบของ Cloud Run

สิ่งที่เราได้พูดถึงไปแล้ว

  • วิธีปรับแต่งโดยใช้ GPU ของ Cloud Run Jobs
  • วิธีแสดงโมเดลโดยใช้ Cloud Run ที่มี vLLM
  • วิธีใช้การกำหนดค่า VPC โดยตรงสำหรับงาน GPU เพื่ออัปโหลดและแสดงโมเดลได้เร็วขึ้น

9 ล้างข้อมูล

เพื่อหลีกเลี่ยงการเรียกเก็บเงินโดยไม่ตั้งใจ เช่น หากมีการเรียกใช้บริการ Cloud Run โดยไม่ตั้งใจมากกว่าการจัดสรรการเรียกใช้ Cloud Run รายเดือนในแพ็กเกจฟรี คุณก็ลบบริการ Cloud Run ที่สร้างขึ้นไว้ในขั้นตอนที่ 6 ได้

หากต้องการลบบริการ Cloud Run ให้ไปที่คอนโซล Cloud ของ Cloud Run ที่ https://console.cloud.google.com/run แล้วลบบริการ serve-gemma-sql

หากต้องการลบทั้งโปรเจ็กต์ ให้ไปที่จัดการทรัพยากร เลือกโปรเจ็กต์ที่สร้างไว้ในขั้นตอนที่ 2 แล้วเลือก "ลบ" หากลบโปรเจ็กต์ คุณจะต้องเปลี่ยนโปรเจ็กต์ใน Cloud SDK คุณดูรายการโปรเจ็กต์ทั้งหมดที่ใช้ได้โดยการเรียกใช้ gcloud projects list