ساخت GKE Inference Gateway چند خوشه‌ای، به همراه TPUها، Cloud Storage FUSE و DRANET مدیریت‌شده

۱. مرور کلی

این آزمایشگاه شما را با زیرساخت هوش مصنوعی که می‌تواند برای اجرای بارهای کاری هوش مصنوعی استفاده شود، آشنا می‌کند. شما با موارد زیر کار خواهید کرد:

موتور گوگل کوبرنتیز (GKE) - پلتفرم اساسی هماهنگ‌سازی کانتینر.

GKE DRANET را مدیریت کرد - شبکه تخصیص منابع پویا که مستقیماً فابریک‌های اتصال پرسرعت را به غلاف‌های TPU شما اختصاص می‌دهد.

GKE Inference Gateway - این یک شیء Gateway مدیریت‌شده از Google Cloud است که برای Inference اقتباس شده است. در این مورد ما از قابلیت‌های چندخوشه‌ای استفاده خواهیم کرد.

واحد پردازش تنسور (TPU) - تراشه‌های شتاب‌دهنده سفارشی گوگل.

Cloud Storage FUSE - یک رابط ذخیره‌سازی که به پادها اجازه می‌دهد تا سطل‌های Cloud Storage را مستقیماً نصب کنند و امکان بارگذاری فوری وزن‌های عظیم مدل را فراهم کنند.

برای پیکربندی، شما یک VPC سفارشی، یک باکت ذخیره‌سازی ابری و دو کلاستر در مناطق مختلف مستقر خواهید کرد. هر کلاستر یک گره TPU خواهد داشت که از DRANET مدیریت‌شده برای شبکه‌سازی خود استفاده می‌کند. پس از افزودن کلاسترها به یک Fleet ، وزن‌های مدل Gemma را در باکت خود ذخیره کرده و یک بار کاری vLLM مستقر می‌کنید که آن وزن‌ها را فوراً از طریق Cloud Storage FUSE نصب می‌کند. در نهایت، GKE Inference Gateway برای مسیریابی ترافیک پیکربندی می‌شود و به شما امکان می‌دهد یک تست failover بین منطقه‌ای را به صورت زنده انجام دهید.

این پیکربندی‌ها از ترکیبی از Terraform ، gcloud و kubectl استفاده خواهند کرد.

در این آزمایشگاه یاد خواهید گرفت که چگونه وظایف زیر را انجام دهید:

  • راه‌اندازی VPC، شبکه‌ها، ذخیره‌سازی
  • تنظیم کلاستر GKE در حالت استاندارد
  • ایجاد گره TPU و استفاده از DRANET مدیریت‌شده
  • اضافه کردن کلاستر به ناوگان
  • وزن‌های مدل کش
  • راه‌اندازی دروازه استنتاج GKE چندخوشه‌ای و تست failover

در این آزمایش، شما قرار است الگوی زیر را ایجاد کنید.

شکل ۱.

52b36edd128f9ffa.png

۲. راه‌اندازی سرویس‌های ابری گوگل

تنظیم محیط خودتنظیم

  1. وارد کنسول گوگل کلود شوید و یک پروژه جدید ایجاد کنید یا از یک پروژه موجود دوباره استفاده کنید. اگر از قبل حساب جیمیل یا گوگل ورک اسپیس ندارید، باید یکی ایجاد کنید .

۲۹۵۰۰۴۸۲۱bab6a87.png

37d264871000675d.png

۹۶d86d3d5655cdbe.png

  • نام پروژه، نام نمایشی برای شرکت‌کنندگان این پروژه است. این یک رشته کاراکتری است که توسط APIهای گوگل استفاده نمی‌شود. شما همیشه می‌توانید آن را به‌روزرسانی کنید.
  • شناسه پروژه در تمام پروژه‌های گوگل کلود منحصر به فرد است و تغییرناپذیر است (پس از تنظیم، قابل تغییر نیست). کنسول کلود به طور خودکار یک رشته منحصر به فرد تولید می‌کند؛ معمولاً برای شما مهم نیست که چه باشد. در اکثر آزمایشگاه‌های کد، باید شناسه پروژه خود را (که معمولاً با عنوان PROJECT_ID شناخته می‌شود) ارجاع دهید. اگر شناسه تولید شده را دوست ندارید، می‌توانید یک شناسه تصادفی دیگر ایجاد کنید. به عنوان یک جایگزین، می‌توانید شناسه خودتان را امتحان کنید و ببینید که آیا در دسترس است یا خیر. پس از این مرحله قابل تغییر نیست و در طول پروژه باقی می‌ماند.
  • برای اطلاع شما، یک مقدار سوم، شماره پروژه ، وجود دارد که برخی از APIها از آن استفاده می‌کنند. برای کسب اطلاعات بیشتر در مورد هر سه این مقادیر، به مستندات مراجعه کنید.
  1. در مرحله بعد، برای استفاده از منابع/API های ابری، باید پرداخت صورتحساب را در کنسول ابری فعال کنید . اجرای این آزمایشگاه کد هزینه زیادی نخواهد داشت، اگر اصلاً هزینه‌ای داشته باشد. برای خاموش کردن منابع به منظور جلوگیری از پرداخت صورتحساب پس از این آموزش، می‌توانید منابعی را که ایجاد کرده‌اید یا پروژه را حذف کنید. کاربران جدید Google Cloud واجد شرایط برنامه آزمایشی رایگان ۳۰۰ دلاری هستند.

شروع پوسته ابری

اگرچه می‌توان از راه دور و از طریق لپ‌تاپ، گوگل کلود را مدیریت کرد، اما در این آزمایشگاه کد، از گوگل کلود شل ، یک محیط خط فرمان که در فضای ابری اجرا می‌شود، استفاده خواهید کرد.

از کنسول گوگل کلود ، روی آیکون Cloud Shell در نوار ابزار بالا سمت راست کلیک کنید:

فعال کردن پوسته ابری

آماده‌سازی و اتصال به محیط فقط چند لحظه طول می‌کشد. وقتی تمام شد، باید چیزی شبیه به این را ببینید:

تصویر صفحه ترمینال Google Cloud Shell که نشان می‌دهد محیط متصل شده است

این ماشین مجازی با تمام ابزارهای توسعه‌ای که نیاز دارید، مجهز شده است. این ماشین مجازی یک دایرکتوری خانگی پایدار ۵ گیگابایتی ارائه می‌دهد و روی فضای ابری گوگل اجرا می‌شود که عملکرد شبکه و احراز هویت را تا حد زیادی بهبود می‌بخشد. تمام کارهای شما در این آزمایشگاه کد را می‌توان در یک مرورگر انجام داد. نیازی به نصب چیزی ندارید.

۳. راه‌اندازی محیط با Terraform

برای انجام این آزمایش، به TPU نیاز دارید. نسخه دقیق مورد استفاده TPU نسخه ۶e است.

  • برای دسترسی باید طبق دستورالعمل طرح TPU عمل کنید و سهمیه TPU را فعال کنید .
  • ما از یک سیستم کوچک استفاده می‌کنیم که به ۴ تراشه TPU v6e ( ct6e-standard-4t) نیاز دارد که به صورت یک برش ۲x۲ در دو ناحیه مختلف خواهد بود.
  • توکن چهره در آغوش گرفته: برای دانلود وزن‌های مدل Gemma به یک توکن دسترسی نیاز است.

ما یک VPC سفارشی با قوانین فایروال، فضای ذخیره‌سازی و زیرشبکه ایجاد خواهیم کرد. کنسول ابری را باز کنید و پروژه‌ای را که استفاده خواهید کرد انتخاب کنید.

  1. Cloud Shell را که در بالای کنسول شما در سمت راست قرار دارد باز کنید، مطمئن شوید که شناسه پروژه صحیح را در Cloud Shell مشاهده می‌کنید، هرگونه درخواستی را برای اجازه دسترسی تأیید کنید. b51b80043d3bac90.png
  2. یک پوشه به نام gke-tf ایجاد کنید و به پوشه مورد نظر بروید.
mkdir -p gke-tf && cd gke-tf
PROJECT_ID=$(gcloud config get-value project)
  1. حالا چند فایل پیکربندی اضافه کنید. این فایل‌ها فایل‌های network.tf ، variable.tf ، providers.tf و fuse.tf زیر را ایجاد می‌کنند.
cat <<EOF > terraform.tfvars
project_id = "${PROJECT_ID}"
EOF

cat <<EOF > variables.tf
variable "project_id" { type = string }
variable "network_prefix" { default = "tpu-gke-dranet" }
variable "regions" { default = ["europe-west4", "us-east5"] }
variable "region_to_tpu_zone" {
  default = {
    "europe-west4" = "europe-west4-a"
    "us-east5"     = "us-east5-b"
  }
}
EOF

cat <<EOF > providers.tf
terraform {
  required_version = ">= 1.5.7"
  required_providers {
    google-beta = { source = "hashicorp/google-beta", version = "~> 7.0" }
    time = { source = "hashicorp/time", version = "~> 0.11.0" }
  }
}
provider "google-beta" { project = var.project_id }

resource "google_project_service" "base_apis" {
  for_each = toset([
    "compute.googleapis.com",
    "container.googleapis.com",
    "cloudresourcemanager.googleapis.com",
    "storage.googleapis.com"
  ])
  project            = var.project_id
  service            = each.value
  disable_on_destroy = false
}
EOF

cat <<EOF > network.tf
resource "google_compute_network" "vpc" {
  name                    = "\${var.network_prefix}-vpc"
  auto_create_subnetworks = false
  mtu                     = 8896 
  depends_on              = [google_project_service.base_apis]
}
resource "google_compute_subnetwork" "subnets" {
  for_each      = toset(var.regions)
  name          = "\${var.network_prefix}-node-subnet" 
  region        = each.value
  network       = google_compute_network.vpc.id
  ip_cidr_range = each.value == "europe-west4" ? "10.0.1.0/24" : "10.0.2.0/24"
}
resource "google_compute_subnetwork" "proxy_subnets" {
  for_each      = toset(var.regions)
  name          = "\${var.network_prefix}-proxy-subnet-\${each.value}"
  region        = each.value
  network       = google_compute_network.vpc.id
  ip_cidr_range = each.value == "europe-west4" ? "10.1.1.0/24" : "10.1.2.0/24"
  purpose       = "GLOBAL_MANAGED_PROXY"
  role          = "ACTIVE"
}
resource "google_compute_address" "gateway_ips" {
  for_each     = toset(var.regions)
  name         = "gemma-gateway-ip-\${each.value}"
  region       = each.value
  subnetwork   = google_compute_subnetwork.subnets[each.value].id
  address_type = "INTERNAL"
}
resource "google_compute_firewall" "allow_internal" {
  name    = "\${var.network_prefix}-allow-internal"
  network = google_compute_network.vpc.name
  allow { protocol = "all" }
  source_ranges = ["10.0.0.0/8", "10.1.0.0/16"]
}
resource "google_compute_firewall" "allow_health_checks" {
  name    = "\${var.network_prefix}-allow-hc"
  network = google_compute_network.vpc.name
  allow { 
    protocol = "tcp"
    ports    = ["8000"] 
  }
  source_ranges = ["130.211.0.0/22", "35.191.0.0/16"]
}
EOF

cat <<EOF > fuse.tf
resource "google_storage_bucket" "model_bucket" {
  name          = "\${var.project_id}-gemma-weights"
  location      = "US" 
  force_destroy = true 
  uniform_bucket_level_access = true
  depends_on    = [google_project_service.base_apis]
}

resource "google_service_account" "gcs_fuse_sa" {
  account_id   = "gcs-fuse-sa"
  display_name = "Service Account for GCS FUSE"
}

resource "google_storage_bucket_iam_member" "gcs_fuse_sa_admin" {
  bucket = google_storage_bucket.model_bucket.name
  role   = "roles/storage.objectAdmin"
  member = "serviceAccount:\${google_service_account.gcs_fuse_sa.email}"
}

resource "google_project_iam_binding" "workload_identity_binding" {
  project = var.project_id
  role    = "roles/iam.workloadIdentityUser"
  members = ["serviceAccount:\${var.project_id}.svc.id.goog[default/gemma-ksa]"]
}
EOF

فایل‌های variable.tf نام پروژه، مناطق و اطلاعات منطقه را اضافه می‌کنند. ps متغیر "regions" که default = ["europe-west4", "us-east5"] را با مناطقی که سهمیه TPU در آنها دارید، به‌روزرسانی کنید. برای اطلاعات بیشتر، این سند " اعتبارسنجی در دسترس بودن TPU در GKE " را بررسی کنید.

فایل network.tf یک VPC جدید به پروژه شما اضافه می‌کند که شامل زیرشبکه‌هایی در دو منطقه مختلف، زیرشبکه‌های فقط پروکسی و قوانین فایروال است.

فایل provider.tf ارائه‌دهنده‌ی مربوطه را برای پشتیبانی از Terraform اضافه می‌کند.

فایل fuse.tf فضای ذخیره‌سازی ابری را برای ذخیره وزن‌های مدل شما اضافه می‌کند و یک حساب کاربری سرویس IAM با مجوزهای objectAdmin فراهم می‌کند. این حساب کاربری به GKE Workload Identity متصل می‌شود.

  1. مطمئن شوید که در دایرکتوری gke-tf هستید و دستورات زیر را اجرا کنید.
    terraform init - دایرکتوری کاری را مقداردهی اولیه می‌کند. این مرحله ارائه‌دهندگان مورد نیاز برای پیکربندی داده شده را دانلود می‌کند. terraform plan - یک طرح اجرایی ایجاد می‌کند که نشان می‌دهد Terraform چه اقداماتی را برای استقرار زیرساخت شما انجام خواهد داد. terraform apply –auto-approve به‌روزرسانی‌ها را اجرا کرده و به‌طور خودکار تأیید می‌کند.
terraform init 
terraform plan 
  1. حالا عملیات نصب را اجرا کنید (این کار ممکن است بین ۳ تا ۵ دقیقه طول بکشد)
terraform apply -auto-approve
  1. در همان پوشه gke-tf فایل gke.tf زیر را ایجاد کنید.
cat <<EOF > gke.tf
resource "google_container_cluster" "clusters" {
  provider = google-beta
  for_each = toset(var.regions)
  name     = "gke-\${each.value}"
  location = var.region_to_tpu_zone[each.value]
  deletion_protection = false
  network             = google_compute_network.vpc.id
  subnetwork          = google_compute_subnetwork.subnets[each.value].id
  release_channel { channel = "RAPID" }
  datapath_provider = "ADVANCED_DATAPATH"
  networking_mode   = "VPC_NATIVE"
  
  gateway_api_config { channel = "CHANNEL_STANDARD" }
  
  ip_allocation_policy {
    cluster_ipv4_cidr_block  = ""
    services_ipv4_cidr_block = ""
  }
  
  workload_identity_config { workload_pool = "\${var.project_id}.svc.id.goog" }
  
  addons_config { 
    gcs_fuse_csi_driver_config { enabled = true } 
  }
  
  initial_node_count = 1
  node_config {
    machine_type = "e2-standard-16"
    oauth_scopes = ["https://www.googleapis.com/auth/cloud-platform"]
    workload_metadata_config { mode = "GKE_METADATA" }
  }
}

resource "google_container_node_pool" "tpu_pools" {
  provider = google-beta
  for_each = toset(var.regions)
  name     = "tpu-v6e-pool"
  location = var.region_to_tpu_zone[each.value]
  cluster  = google_container_cluster.clusters[each.value].name
  node_count = 1
  
  network_config { accelerator_network_profile = "auto" }
  
  node_config {
    machine_type = "ct6e-standard-4t"
    oauth_scopes = ["https://www.googleapis.com/auth/cloud-platform"]    
    labels = { "cloud.google.com/gke-networking-dra-driver" = "true" }
    workload_metadata_config { mode = "GKE_METADATA" }
  }
  
  lifecycle { ignore_changes = [node_config[0].labels] }
}
EOF

gke.tf دو خوشه را در مناطق مختلف اضافه می‌کند و دو گره TPU ایجاد می‌کند که TPU v6e را با ۴ تراشه اجرا می‌کنند و DRANET مدیریت‌شده را به گره‌های گره اختصاص می‌دهد.

  1. حالا عملیات نصب را اجرا کنید (این کار ممکن است بین ۱۰ تا ۱۵ دقیقه طول بکشد)
terraform apply -auto-approve
  1. تأیید
echo -e "\n=== Verifying GKE Clusters ==="
gcloud container clusters list --filter="name:gke-europe-west4 OR name:gke-us-east5" --project=$PROJECT_ID

echo -e "\n=== Verifying VPC Network ==="
gcloud compute networks list --filter="name:tpu-gke-dranet-vpc" --project=$PROJECT_ID

echo -e "\n=== Verifying Reserved Static IPs for Gateway ==="
gcloud compute addresses list --filter="name~gemma-gateway-ip" --project=$PROJECT_ID

echo -e "\n=== Verifying GCS Bucket ==="
gcloud storage ls | grep "${PROJECT_ID}-gemma-weights"

echo -e "\n=== Verifying GCS FUSE Service Account ==="
gcloud iam service-accounts list --filter="email:gcs-fuse-sa@${PROJECT_ID}.iam.gserviceaccount.com" --project=$PROJECT_ID

۴. ثبت ناوگان

ما باید خوشه را در یک ناوگان ثبت کنیم.

  1. مطمئن شوید که در دایرکتوری gke-tf هستید و دستورات زیر را اجرا کنید.
cat <<EOF > fleet.tf
data "google_project" "project" {
  project_id = var.project_id
}

resource "google_project_service" "fleet_apis" {
  for_each = toset([
    "gkehub.googleapis.com",
    "multiclusterservicediscovery.googleapis.com",
    "multiclusteringress.googleapis.com",
    "trafficdirector.googleapis.com"
  ])
  project            = var.project_id
  service            = each.value
  disable_on_destroy = false
}

resource "google_project_service_identity" "mci_sa" {
  provider = google-beta
  project  = var.project_id
  service  = "multiclusteringress.googleapis.com"
  depends_on = [google_project_service.fleet_apis]
}

resource "time_sleep" "wait_for_apis" {
  create_duration = "60s"
  depends_on      = [google_project_service.fleet_apis]
}

resource "google_project_iam_member" "mci_sa_admin" {
  project    = var.project_id
  role       = "roles/container.admin"
  member     = "serviceAccount:\${google_project_service_identity.mci_sa.email}"
  depends_on = [google_project_service_identity.mci_sa, time_sleep.wait_for_apis]
}

resource "google_gke_hub_membership" "memberships" {
  provider      = google-beta
  for_each      = toset(var.regions)
  project       = var.project_id
  membership_id = "gke-\${each.value}"
  endpoint {
    gke_cluster { resource_link = "//container.googleapis.com/\${google_container_cluster.clusters[each.value].id}" }
  }
  depends_on = [time_sleep.wait_for_apis, google_container_cluster.clusters]
}

resource "google_gke_hub_feature" "mcs" {
  provider   = google-beta
  name       = "multiclusterservicediscovery"
  location   = "global"
  project    = var.project_id
  depends_on = [time_sleep.wait_for_apis]
}

resource "google_gke_hub_feature" "ingress" {
  provider   = google-beta
  name       = "multiclusteringress"
  location   = "global"
  project    = var.project_id
  depends_on = [google_gke_hub_membership.memberships, google_project_iam_member.mci_sa_admin]
  spec {
    multiclusteringress { config_membership = "projects/\${var.project_id}/locations/global/memberships/gke-us-east5" }
  }
}
EOF

فایل fleet.tf هر دو خوشه را در یک ناوگان جهانی GKE ثبت می‌کند و امکان کشف و ورود سرویس چندخوشه‌ای را فراهم می‌کند. این فایل، خوشه ایالات متحده را به عنوان خوشه پیکربندی مرکزی تعیین می‌کند و به Gateway API اجازه می‌دهد تا ترافیک را نظارت و مسیریابی کند.

  1. در پوشه gke-tf قرار دهید و اجرا کنید (این کار باید ۳-۵ دقیقه طول بکشد)
terraform plan 
terraform apply -auto-approve
  1. ثبت نام ناوگان را تأیید کنید
gcloud container fleet memberships list --project=$PROJECT_ID

۵. وزن‌های مدل را در FUSE ذخیره کنید

ما یک Kubernetes Job موقت در کلاستر ایالات متحده اجرا خواهیم کرد تا مدل Gemma را به طور ایمن از طریق یک اسکریپت پایتون مستقیماً در مخزن ذخیره‌سازی ابری نصب شده روی FUSE دانلود کنیم.

  1. متغیرهای زیر را ایجاد کنید
export CTX_EU="gke_${PROJECT_ID}_europe-west4-a_gke-europe-west4"
export CTX_US="gke_${PROJECT_ID}_us-east5-b_gke-us-east5"
  1. این از مدل google/gemma-3-27b-it استفاده می‌کند، بنابراین باید یک توکن HF ایجاد کنید . YOUR_ACTUAL_HUGGING_FACE_TOKEN را در زیر با توکن واقعی خود جایگزین کنید.
export HF_TOKEN="YOUR_ACTUAL_HUGGING_FACE_TOKEN"
  1. مطمئن شوید که در دایرکتوری gke-tf هستید و دستورات زیر را اجرا کنید.
gcloud container clusters get-credentials gke-us-east5 --zone us-east5-b --project=$PROJECT_ID

cat <<EOF > ksa.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: gemma-ksa
  namespace: default
  annotations:
    iam.gke.io/gcp-service-account: "gcs-fuse-sa@${PROJECT_ID}.iam.gserviceaccount.com"
EOF

kubectl apply -f ksa.yaml --context=$CTX_US
kubectl delete secret hf-secret --context=$CTX_US --ignore-not-found
kubectl create secret generic hf-secret --from-literal=hf_token=${HF_TOKEN} --context=$CTX_US

cat <<EOF > download-job.yaml
apiVersion: batch/v1
kind: Job
metadata:
  name: model-downloader
  namespace: default
spec:
  backoffLimit: 1
  template:
    metadata:
      annotations:
        gke-gcsfuse/volumes: "true"
    spec:
      serviceAccountName: gemma-ksa
      restartPolicy: Never
      containers:
      - name: downloader
        image: python:3.11-slim
        env:
        - name: HF_TOKEN
          valueFrom:
            secretKeyRef:
              name: hf-secret
              key: hf_token
        command:
        - bash
        - -c
        - |
          pip install -U huggingface_hub
          echo "Downloading Gemma 3 directly to GCS bucket..."
          python3 -c "from huggingface_hub import snapshot_download; import os; snapshot_download(repo_id='google/gemma-3-27b-it', local_dir='/data/gemma-weights', token=os.environ['HF_TOKEN'])"
          echo "Download complete! Safe to proceed."
        volumeMounts:
        - name: gcs-fuse-volume
          mountPath: /data/gemma-weights
      volumes:
      - name: gcs-fuse-volume
        csi:
          driver: gcsfuse.csi.storage.gke.io
          volumeAttributes:
            bucketName: "${PROJECT_ID}-gemma-weights"
EOF

kubectl apply -f download-job.yaml --context=$CTX_US
  1. قبل از ادامه، صبر کنید تا دانلود تمام شود (این کار بسته به اندازه مدل، ۵ تا ۱۰ دقیقه طول می‌کشد)
kubectl logs -f job/model-downloader --context=$CTX_US

(برای خروج از گزارش‌ها، پس از نمایش پیام «دانلود کامل شد!» Ctrl+C را فشار دهید.)

۶. استقرار حجم کاری vLLM و Gemma

  1. مطمئن شوید که در دایرکتوری gke-tf هستید و دستورات زیر را اجرا کنید.
cat <<EOF > workload.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: gemma-ksa
  namespace: default
  annotations:
    iam.gke.io/gcp-service-account: "gcs-fuse-sa@${PROJECT_ID}.iam.gserviceaccount.com"
---
apiVersion: resource.k8s.io/v1
kind: ResourceClaimTemplate
metadata:
  name: all-netdev
  namespace: default
spec:
  spec:
    devices:
      requests:
      - name: req-netdev
        exactly:
          deviceClassName: netdev.google.com
          allocationMode: All
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: vllm-gemma
  namespace: default
  labels:
    app: gemma-server
spec:
  replicas: 1
  selector:
    matchLabels:
      app: gemma-server
  template:
    metadata:
      labels:
        app: gemma-server
      annotations:
        gke-gcsfuse/volumes: "true"
    spec:
      serviceAccountName: gemma-ksa
      nodeSelector:
        cloud.google.com/gke-tpu-accelerator: tpu-v6e-slice
        cloud.google.com/gke-tpu-topology: 2x2
      resourceClaims:
      - name: netdev
        resourceClaimTemplateName: all-netdev
      containers:
      - name: vllm-tpu
        image: vllm/vllm-tpu:latest
        command:
        - bash
        - -c
        - |
          export PYTHONUNBUFFERED=1
          echo "Booting vLLM instantly from local GCS FUSE mount..."
          
          python3 -m vllm.entrypoints.openai.api_server \
            --model /data/gemma-weights \
            --tensor-parallel-size 4 \
            --port 8000
        ports:
        - containerPort: 8000
        resources:
          requests:
            google.com/tpu: 4
          limits:
            google.com/tpu: 4
          claims:
          - name: netdev
        volumeMounts:
        - name: dshm
          mountPath: /dev/shm
        - name: gcs-fuse-volume
          mountPath: /data/gemma-weights
          readOnly: true
      volumes:
      - name: dshm
        emptyDir:
          medium: Memory
      - name: gcs-fuse-volume
        csi:
          driver: gcsfuse.csi.storage.gke.io
          readOnly: true
          volumeAttributes:
            bucketName: "${PROJECT_ID}-gemma-weights"
            mountOptions: "implicit-dirs"
            fileCacheCapacity: "100Gi"
            fileCacheForRangeRead: "true"
---
apiVersion: v1
kind: Service
metadata:
  name: vllm-gemma-service
  namespace: default
spec:
  selector:
    app: gemma-server
  ports:
  - protocol: TCP
    port: 8000
    targetPort: 8000
  type: ClusterIP
---
apiVersion: monitoring.googleapis.com/v1
kind: PodMonitoring
metadata:
  name: vllm-gemma-monitoring
  namespace: default
spec:
  selector:
    matchLabels:
      app: gemma-server
  endpoints:
  - port: 8000
    interval: 15s
    path: /metrics
EOF
  1. حالا اسکریپت زیر را اجرا کنید (این کار بین ۵ تا ۱۰ دقیقه طول می‌کشد زیرا در دو منطقه مستقر می‌شود)
for CTX in $CTX_EU $CTX_US; do
  ZONE=$(echo $CTX | cut -d_ -f3)
  CLUSTER=$(echo $CTX | cut -d_ -f4)
  gcloud container clusters get-credentials $CLUSTER --zone $ZONE --project=$PROJECT_ID
  
  kubectl delete secret hf-secret --ignore-not-found --context=$CTX
  kubectl create secret generic hf-secret --from-literal=hf_token=${HF_TOKEN} --context=$CTX
  kubectl apply -f workload.yaml --context=$CTX
done
  1. تأیید استقرار
for CTX in $CTX_EU $CTX_US; do kubectl rollout status deployment/vllm-gemma --timeout=15m --context=$CTX; done
  1. پس از تکمیل، می‌توانید با اجرای دستور زیر، تأیید کنید که شبکه مدیریت‌شده DRANET به پادها اختصاص داده شده است.
for CTX in $CTX_EU $CTX_US; do
  echo "Checking DRA network interfaces on $CTX..."
  kubectl --context=$CTX exec deployment/vllm-gemma -c vllm-tpu -- ls /sys/class/net
  echo "----------------------------------------"
done

رابط‌های شبکه اضافی eth0 را برای شبکه‌بندی استاندارد پاد، در کنار رابط‌های ثانویه که نشان‌دهنده ساختار TPU اختصاصی شما eth1، eth2 و غیره هستند، مشاهده خواهید کرد.

۷. API استنتاج و پیکربندی دروازه

اکنون InferenceObjective (gemma-objective), AutoscalingMetric (tpu-cache) و Inference Pool(gemma-pool) را ایجاد خواهید کرد. Inference Pool با استفاده از نمودار Helm ایجاد می‌شود. این نمودار نصب و اعتبارسنجی می‌شود.

  1. مطمئن شوید که در دایرکتوری gke-tf هستید و دستورات زیر را اجرا کنید. این کار شیء را مستقر کرده و اعتبارسنجی را اجرا می‌کند.
cat <<EOF > inference-objective.yaml
apiVersion: inference.networking.x-k8s.io/v1alpha2
kind: InferenceObjective
metadata:
  name: gemma-objective
  namespace: default
spec:
  priority: 10
  poolRef:
    name: gemma-pool
    group: "inference.networking.k8s.io" 
EOF

cat <<EOF > metrics.yaml
apiVersion: autoscaling.gke.io/v1beta1
kind: AutoscalingMetric
metadata:
  name: tpu-cache
  namespace: default
spec:
  selector:
    matchLabels:
      app: gemma-server
  endpoints:
  - port: 8000
    path: /metrics
    metrics:
    - name: vllm:kv_cache_usage_perc
      exportName: tpu-cache
EOF

for CTX in $CTX_EU $CTX_US; do
  kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api-inference-extension/v1.1.0/config/crd/bases/inference.networking.x-k8s.io_inferenceobjectives.yaml --context=$CTX
  kubectl apply -f inference-objective.yaml --context=$CTX
  kubectl apply -f metrics.yaml --context=$CTX
done

helm install gemma-pool --kube-context $CTX_EU \
  --set inferencePool.modelServers.matchLabels.app=gemma-server \
  --set provider.name=gke \
  --version v1.1.0 \
  oci://registry.k8s.io/gateway-api-inference-extension/charts/inferencepool

helm install gemma-pool --kube-context $CTX_US \
  --set inferencePool.modelServers.matchLabels.app=gemma-server \
  --set provider.name=gke \
  --set inferenceExtension.monitoring.gke.enabled=true \
  --version v1.1.0 \
  oci://registry.k8s.io/gateway-api-inference-extension/charts/inferencepool

for CTX in $CTX_EU $CTX_US; do
  kubectl annotate inferencepool gemma-pool networking.gke.io/export="True" --context=$CTX
done

for CTX in $CTX_EU $CTX_US; do
  echo "Verifying Inference API resources on $CTX..."
  kubectl get inferencepools --context=$CTX
  kubectl get autoscalingmetrics tpu-cache --context=$CTX
done

۸. پیکربندی دروازه

اکنون پیکربندی Cross-Regional Gateway را ایجاد خواهید کرد. Gateway(cross-region-gateway), HTTPRoute (gemma-route) , HealthCheckPolicy(gemma-health-check)and GCPBackendPolicy(gemma-backend-policy. مجموعه استنتاج با استفاده از نمودار Helm ایجاد می‌شود. این مجموعه نصب و اعتبارسنجی می‌شود. (فعال شدن Gateway 8 تا 10 دقیقه طول می‌کشد)

cat <<EOF > config-cluster.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: cross-region-gateway
  namespace: default
spec:
  gatewayClassName: gke-l7-cross-regional-internal-managed-mc
  addresses:
  - type: networking.gke.io/named-address-with-region
    value: "regions/europe-west4/addresses/gemma-gateway-ip-europe-west4"
  - type: networking.gke.io/named-address-with-region
    value: "regions/us-east5/addresses/gemma-gateway-ip-us-east5"
  listeners:
  - name: http
    protocol: HTTP
    port: 80
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: gemma-route
  namespace: default
spec:
  parentRefs:
  - name: cross-region-gateway
    kind: Gateway
  rules:
  - backendRefs:
    - group: networking.gke.io
      kind: GCPInferencePoolImport
      name: gemma-pool
      port: 8000
---
apiVersion: networking.gke.io/v1
kind: HealthCheckPolicy
metadata:
  name: gemma-health-check
  namespace: default
spec:
  targetRef:
    group: networking.gke.io
    kind: GCPInferencePoolImport
    name: gemma-pool
  default:
    config:
      type: HTTP
      httpHealthCheck:
        requestPath: /health
        port: 8000
---
apiVersion: networking.gke.io/v1
kind: GCPBackendPolicy
metadata:
  name: gemma-backend-policy
  namespace: default
spec:
  targetRef:
    group: networking.gke.io
    kind: GCPInferencePoolImport
    name: gemma-pool
  default:
    timeoutSec: 100
    balancingMode: CUSTOM_METRICS
    trafficDuration: LONG
    customMetrics:
      - name: gke.named_metrics.tpu-cache
        dryRun: false
        maxUtilizationPercent: 60
EOF

echo -e "\n=== Creating Cross-Regional Gateway Resources ==="
kubectl apply -f config-cluster.yaml --context=$CTX_US

echo -e "\n=== Provisioning Global Load Balancer (This takes 5-10 minutes) ==="
echo "Working on the Gateway... waiting for Google Cloud to assign IPs and program routes..."

# The script will hold here until the gateway is officially ready
kubectl wait --for=condition=programmed gateway/cross-region-gateway --timeout=10m --context=$CTX_US

echo -e "\n=== SUCCESS: Gateway is fully provisioned and ready! ==="

استخر استنتاج (Helm) : سرورهای مدل شما را از هر دو منطقه در یک backend منطقی واحد گروه‌بندی می‌کند.

Gateway & HTTPRoute : متعادل‌کننده بار داخلی سراسری واقعی را ایجاد می‌کند و قوانینی را برای مسیریابی درخواست‌های هوش مصنوعی ورودی به مدل‌های شما تعریف می‌کند.

سیاست‌های بررسی سلامت و بک‌اند : تضمین می‌کند که درخواست‌ها فقط به پادهای سالم ارسال می‌شوند و توزیع هوشمند و مبتنی بر معیار ترافیک را امکان‌پذیر می‌سازد (از بارگذاری بیش از حد TPUها جلوگیری می‌کند).

اعتبارسنجی : اسکریپت متوقف می‌شود تا مطمئن شود که Google Cloud قبل از ادامه، آدرس‌های IP داخلی را به طور کامل تأمین کرده است.

۹. تست غلبه بر خرابی

حالا به بهترین بخش آزمایشگاه می‌رسیم: آزمایش میزان دسترسی‌پذیری بالای معماری شما.

دقیقاً کاری که این تست خودکار انجام خواهد داد به شرح زیر است:

  • آزمون پایه: کاربر شبیه‌سازی‌شده ما یک درخواست استنتاج ("پایتخت فرانسه کجاست؟") ارسال می‌کند. از آنجا که کاربر در منطقه اصلی قرار دارد، Gateway درخواست را برای کمترین تأخیر ممکن به آن TPUهای محلی هدایت می‌کند.
  • فاجعه: ما با از کار انداختن تمام پادهای TPU در ناحیه اصلی ( replicas=0 ) یک قطعی فاجعه‌بار در مرکز داده را شبیه‌سازی می‌کنیم.
  • تشخیص: ما ۴۵ ثانیه صبر می‌کنیم. در طول این پنجره، بررسی‌های سلامت Gateway با شکست مواجه می‌شود، متوجه می‌شود که backend اصلی کاملاً آفلاین است و جداول مسیریابی سراسری خود را به صورت پویا به‌روزرسانی می‌کند.
  • Failover: کاربر ما درخواست دوم را ارسال می‌کند ("پایتخت آلمان کجاست؟"). کاربر اصلاً نمی‌داند که قطعی وجود دارد. Gateway درخواست را رهگیری می‌کند و فوراً آن را در سراسر جهان به TPU های ثانویه سالم شما هدایت می‌کند.
  • بازیابی: ما TPU های اولیه را بازیابی می‌کنیم و معماری سراسری شما را به سلامت کامل بازمی‌گردانیم.
  1. Cloud Shell را باز کنید و دستور زیر را اجرا کنید:
cat << 'EOF' > failover-test.sh
#!/bin/bash
# Multi-Cluster Inference Failover Test

export PROJECT_ID=$(gcloud config get-value project)
export CTX_EU="gke_${PROJECT_ID}_europe-west4-a_gke-europe-west4"
export CTX_US="gke_${PROJECT_ID}_us-east5-b_gke-us-east5"

echo -e "\n=== PHASE 1: VERIFYING CURRENT STATE (BOTH CLUSTERS UP) ==="
echo "Checking US Cluster (Primary):"
kubectl get pods -l app=gemma-server --context=$CTX_US
echo "Checking EU Cluster (Secondary):"
kubectl get pods -l app=gemma-server --context=$CTX_EU

echo -e "\nDeploying Test Client in US..."
export GATEWAY_IP_US=$(gcloud compute addresses describe gemma-gateway-ip-us-east5 --region=us-east5 --project=$PROJECT_ID --format="value(address)")

kubectl run curl-test --image=curlimages/curl --restart=Never --context=$CTX_US -- sleep 3600
kubectl wait --for=condition=ready pod/curl-test --context=$CTX_US --timeout=60s

echo -e "\n=== PHASE 2: BASELINE TEST (US Client -> US TPUs) ==="
echo "Prompting the AI: 'What is the capital of France?'"
echo "Expect to see the full JSON response including token usage..."
kubectl exec curl-test --context=$CTX_US -- curl -s -X POST http://$GATEWAY_IP_US/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "/data/gemma-weights", 
    "messages": [{"role": "user", "content": "What is the capital of France?"}], 
    "max_tokens": 100
  }' | jq .

echo -e "\n=== PHASE 3: SIMULATING REGIONAL OUTAGE (Scaling US to 0) ==="
kubectl scale deployment vllm-gemma --replicas=0 --context=$CTX_US
echo "Waiting 20 seconds for pods to begin terminating..."
sleep 20

echo -e "\n=== PHASE 4: CONFIRMING STATE (PODS TERMINATING) ==="
echo "Checking US Cluster (Should be terminating):"
kubectl get pods -l app=gemma-server --context=$CTX_US
echo "Checking EU Cluster (Should still be running):"
kubectl get pods -l app=gemma-server --context=$CTX_EU

echo -e "\nWaiting 45 seconds for Gateway health checks to update global routing tables..."
sleep 45

echo -e "\n=== PHASE 5: CONFIRMING COMPLETE DOWN AND EURO UP ==="
echo "Checking US Cluster (Should be completely empty now):"
kubectl get pods -l app=gemma-server --context=$CTX_US
echo "Checking EU Cluster (Should still be running):"
kubectl get pods -l app=gemma-server --context=$CTX_EU

echo -e "\n=== PHASE 6: FAILOVER TEST (US Client -> EU TPUs) ==="
echo "Prompting the AI: 'What is the capital of Germany?'"
echo "Request is actively being rerouted to Europe. Expecting full JSON response..."
kubectl exec curl-test --context=$CTX_US -- curl -s -X POST http://$GATEWAY_IP_US/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "/data/gemma-weights", 
    "messages": [{"role": "user", "content": "What is the capital of Germany?"}], 
    "max_tokens": 100
  }' | jq .

echo -e "\n=== PHASE 7: RESTORING INFRASTRUCTURE (Scaling US to 1) ==="
kubectl scale deployment vllm-gemma --replicas=1 --context=$CTX_US
echo "Waiting for US pods to boot and mount FUSE..."
kubectl rollout status deployment/vllm-gemma --timeout=15m --context=$CTX_US

echo -e "\n=== PHASE 8: CONFIRMING BOTH SYSTEMS ARE BACK UP ==="
echo "Checking US Cluster (Restored):"
kubectl get pods -l app=gemma-server --context=$CTX_US
echo "Checking EU Cluster (Still Healthy):"
kubectl get pods -l app=gemma-server --context=$CTX_EU

echo -e "\n=== PHASE 9: CLEANUP ==="
kubectl delete pod curl-test --context=$CTX_US
echo "Failover lab complete."
EOF

chmod +x failover-test.sh
./failover-test.sh
  1. پس از اتمام آزمایش، می‌توانید آن را تمیز کنید.

۱۰. تمیز کردن

  1. حجم کار را تمیز کنید
#!/bin/bash
echo "=== PART 1: Kubernetes & Workload Cleanup ==="
export PROJECT_ID=$(gcloud config get-value project)
export CTX_EU="gke_${PROJECT_ID}_europe-west4-a_gke-europe-west4"
export CTX_US="gke_${PROJECT_ID}_us-east5-b_gke-us-east5"

echo "Deleting Gateway resources..."
for CTX in $CTX_EU $CTX_US; do
  kubectl delete gateways,httproutes,healthcheckpolicies,gcpbackendpolicies --all --context=$CTX --ignore-not-found
done

echo "Waiting 60 seconds for the external Load Balancer to detach..."
sleep 60

echo "Cleaning up workloads and custom resources..."
for CTX in $CTX_EU $CTX_US; do
  helm uninstall gemma-pool --kube-context=$CTX || true
  kubectl delete job model-downloader --context=$CTX --ignore-not-found
  kubectl delete all -l app=gemma-server --context=$CTX --ignore-not-found
  kubectl delete inferenceobjectives,autoscalingmetrics --all --context=$CTX --ignore-not-found
  kubectl delete serviceaccount gemma-ksa --context=$CTX --ignore-not-found
  kubectl delete -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api-inference-extension/v1.1.0/config/crd/bases/inference.networking.x-k8s.io_inferenceobjectives.yaml --context=$CTX --ignore-not-found
done

echo -e "\n=== Part 1 Complete! Safe to proceed to Terraform Teardown. ==="
  1. زیرساخت را پاکسازی کنید. مطمئن شوید که در پوشه gke-tf هستید.
cat << 'EOF' > cleanup-tf.sh
#!/bin/bash
echo "=== PART 2: Infrastructure & Terraform Teardown ==="
export PROJECT_ID=$(gcloud config get-value project)
export LAB_NETWORK="tpu-gke-dranet-vpc"

echo "Destroying GKE Fleet Features to prevent firewall resurrection..."
terraform destroy -target=google_gke_hub_feature.mcs -target=google_gke_hub_feature.ingress -auto-approve

echo "Waiting 30 seconds for the self-healing controllers to spin down..."
sleep 30

echo "Hunting down orphaned auto-generated firewall rules strictly on the lab network..."
GHOST_RULES=$(gcloud compute firewall-rules list --filter="network~${LAB_NETWORK} AND (name~mcsd OR name~k8s-fw-l7)" --format="value(name)" --project=$PROJECT_ID)

if [ ! -z "$GHOST_RULES" ]; then
  for rule in $GHOST_RULES; do
    echo "Deleting ghost rule: $rule"
    gcloud compute firewall-rules delete $rule --project=$PROJECT_ID --quiet
  done
else
  echo "No ghost rules found on ${LAB_NETWORK}."
fi

echo "=== Controllers and Firewalls dead. Destroying remaining Base Infrastructure. ==="

MAX_RETRIES=3
RETRY_COUNT=0
SUCCESS=false

while [ $RETRY_COUNT -lt $MAX_RETRIES ]; do
  # Run the destroy command. If it succeeds (exit code 0), break the loop.
  if terraform destroy -auto-approve; then
    SUCCESS=true
    break
  else
    RETRY_COUNT=$((RETRY_COUNT+1))
    echo -e "\n[WARNING] Terraform destroy encountered an error (likely a GCP resource lock)."
    
    if [ $RETRY_COUNT -lt $MAX_RETRIES ]; then
      echo "Waiting 30 seconds before retry $RETRY_COUNT of $MAX_RETRIES..."
      sleep 30
    fi
  fi
done

if [ "$SUCCESS" = true ]; then
  echo -e "\n=== Lab Cleanup Successfully Completed! ==="
else
  echo -e "\n[ERROR] Lab Cleanup failed after $MAX_RETRIES attempts."
  echo "Some resources may still be locked. Run 'terraform destroy -auto-approve' manually later to finish."
  exit 1
fi
EOF

chmod +x cleanup-tf.sh
./cleanup-tf.sh

اگر در حذف منابع خاص با مشکلی مواجه شدید، باید اسکریپت دستور terraform destroy ./cleanup-tf.sh را دوباره اجرا کنید.

۱۱. تبریک

تبریک! شما با موفقیت یک GKE Inference Gateway چندخوشه‌ای با قابلیت دسترسی بالا و معماری استنتاج هوش مصنوعی بین منطقه‌ای را با استفاده از GKE، شتاب‌دهنده‌های DRANET مدیریت‌شده و TPU v6e مستقر کرده‌اید.

با ترکیب Cloud Storage FUSE برای بارگذاری فوری مدل و Inference Gateway API برای مسیریابی چندخوشه‌ای و آگاه از تأخیر، شما یک backend انعطاف‌پذیر ساخته‌اید که قادر است از قطع کامل مرکز داده منطقه‌ای بدون افت ترافیک کاربر داخلی جان سالم به در ببرد.

مراحل بعدی / اطلاعات بیشتر

می‌توانید درباره شبکه‌سازی GKE بیشتر بخوانید

آزمایشگاه بعدی خود را انجام دهید

به جستجوی خود با Google Cloud ادامه دهید و این آزمایشگاه‌های دیگر Google Cloud را بررسی کنید: