สร้าง GKE Inference Gateway แบบหลายคลัสเตอร์ด้วย TPU, Cloud Storage FUSE และ DRANET ที่มีการจัดการ

1. ภาพรวม

แล็บนี้จะแนะนำให้คุณรู้จักโครงสร้างพื้นฐาน AI ที่ใช้เรียกใช้งานภาระงาน AI ได้ คุณจะต้องทำงานร่วมกับสิ่งต่อไปนี้

Google Kubernetes Engine (GKE) - แพลตฟอร์มการจัดการคอนเทนเนอร์เป็นกลุ่มพื้นฐาน

DRANET ที่มีการจัดการของ GKE - การเชื่อมต่อเครือข่ายการจัดสรรทรัพยากรแบบไดนามิกที่กำหนดโครงสร้างการเชื่อมต่อความเร็วสูงให้กับพ็อด TPU โดยตรง

GKE Inference Gateway - นี่คือออบเจ็กต์เกตเวย์ที่มีการจัดการจาก Google Cloud ซึ่งปรับให้เหมาะกับการอนุมาน ในกรณีนี้ เราจะใช้ความสามารถแบบหลายคลัสเตอร์

Tensor Processing Unit (TPU) - ชิปเร่งความเร็วที่ Google สร้างขึ้นเอง

Cloud Storage FUSE - อินเทอร์เฟซการจัดเก็บที่ช่วยให้พ็อดต่อเชื่อม Bucket ของ Cloud Storage ได้โดยตรง ซึ่งจะช่วยให้โหลดน้ำหนักของโมเดลขนาดใหญ่ได้ทันที

หากต้องการกำหนดค่า คุณจะต้องทำให้ใช้งานได้ VPC ที่กำหนดเอง, Bucket ของ Cloud Storage และคลัสเตอร์ 2 รายการในภูมิภาคต่างๆ แต่ละคลัสเตอร์จะมี Node Pool ของ TPU ที่ใช้ DRANET ที่มีการจัดการสำหรับการเชื่อมต่อเครือข่าย หลังจากเพิ่มคลัสเตอร์ลงใน Fleet แล้ว คุณจะแคชน้ำหนักของโมเดล Gemma ใน Bucket และทำให้ใช้งานได้ภาระงาน vLLM ที่ต่อเชื่อมน้ำหนักเหล่านั้นทันทีผ่าน Cloud Storage FUSE สุดท้ายนี้ ระบบจะกำหนดค่า GKE Inference Gateway เพื่อกำหนดเส้นทางการรับส่งข้อมูล ซึ่งจะช่วยให้คุณทำการทดสอบการทำงานล้มเหลวข้ามภูมิภาคแบบสดได้

การกำหนดค่าจะใช้ร่วมกันระหว่าง Terraform, gcloud และ kubectl

ในแล็บนี้ คุณจะได้เรียนรู้วิธีทำงานต่อไปนี้

  • ตั้งค่า VPC, เครือข่าย, พื้นที่เก็บข้อมูล
  • ตั้งค่าคลัสเตอร์ GKE ในโหมดมาตรฐาน
  • สร้าง Node Pool ของ TPU และใช้ DRANET ที่มีการจัดการ
  • เพิ่มคลัสเตอร์ไปยังฟลีท
  • น้ำหนักของโมเดลแคช
  • ตั้งค่าเกตเวย์การอนุมาน GKE แบบหลายคลัสเตอร์และทดสอบการทำงานเมื่อเกิดข้อผิดพลาด

ในแล็บนี้ คุณจะได้สร้างรูปแบบต่อไปนี้

รูปที่ 1

52b36edd128f9ffa.png

2. การตั้งค่าบริการ Google Cloud

การตั้งค่าสภาพแวดล้อมแบบเรียนรู้ด้วยตนเอง

  1. ลงชื่อเข้าใช้ Google Cloud Console แล้วสร้างโปรเจ็กต์ใหม่หรือใช้โปรเจ็กต์ที่มีอยู่ซ้ำ หากยังไม่มีบัญชี Gmail หรือ Google Workspace คุณต้องสร้างบัญชี

295004821bab6a87.png

37d264871000675d.png

96d86d3d5655cdbe.png

  • ชื่อโปรเจ็กต์คือชื่อที่แสดงสำหรับผู้เข้าร่วมโปรเจ็กต์นี้ ซึ่งเป็นสตริงอักขระที่ Google APIs ไม่ได้ใช้ คุณอัปเดตได้ทุกเมื่อ
  • รหัสโปรเจ็กต์จะไม่ซ้ำกันในโปรเจ็กต์ Google Cloud ทั้งหมดและเปลี่ยนแปลงไม่ได้ (เปลี่ยนไม่ได้หลังจากตั้งค่าแล้ว) Cloud Console จะสร้างสตริงที่ไม่ซ้ำกันโดยอัตโนมัติ ซึ่งโดยปกติแล้วคุณไม่จำเป็นต้องสนใจว่าสตริงนั้นคืออะไร ใน Codelab ส่วนใหญ่ คุณจะต้องอ้างอิงรหัสโปรเจ็กต์ (โดยทั่วไปจะระบุเป็น PROJECT_ID) หากไม่ชอบรหัสที่สร้างขึ้น คุณอาจสร้างรหัสแบบสุ่มอีกรหัสหนึ่งได้ หรือคุณอาจลองใช้ชื่อของคุณเองและดูว่ามีชื่อนั้นหรือไม่ คุณจะเปลี่ยนแปลงรหัสนี้หลังจากขั้นตอนนี้ไม่ได้ และรหัสจะคงอยู่ตลอดระยะเวลาของโปรเจ็กต์
  • โปรดทราบว่ายังมีค่าที่ 3 ซึ่งคือหมายเลขโปรเจ็กต์ที่ API บางตัวใช้ ดูข้อมูลเพิ่มเติมเกี่ยวกับค่าทั้ง 3 นี้ได้ในเอกสารประกอบ
  1. จากนั้นคุณจะต้องเปิดใช้การเรียกเก็บเงินใน Cloud Console เพื่อใช้ทรัพยากร/API ของ Cloud การทำตาม Codelab นี้จะไม่มีค่าใช้จ่ายมากนัก หรืออาจไม่มีค่าใช้จ่ายเลย หากต้องการปิดทรัพยากรเพื่อหลีกเลี่ยงการเรียกเก็บเงินนอกเหนือจากบทแนะนำนี้ คุณสามารถลบทรัพยากรที่สร้างขึ้นหรือลบโปรเจ็กต์ได้ ผู้ใช้ Google Cloud รายใหม่มีสิทธิ์เข้าร่วมโปรแกรมช่วงทดลองใช้ฟรีมูลค่า$300 USD

เริ่มต้น Cloud Shell

แม้ว่าคุณจะใช้งาน Google Cloud จากระยะไกลจากแล็ปท็อปได้ แต่ใน Codelab นี้คุณจะใช้ Google Cloud Shell ซึ่งเป็นสภาพแวดล้อมบรรทัดคำสั่งที่ทำงานในระบบคลาวด์

จาก Google Cloud Console ให้คลิกไอคอน Cloud Shell ในแถบเครื่องมือด้านขวาบน

เปิดใช้งาน Cloud Shell

การจัดสรรและเชื่อมต่อกับสภาพแวดล้อมจะใช้เวลาเพียงไม่กี่นาที เมื่อเสร็จแล้ว คุณควรเห็นข้อความคล้ายกับตัวอย่างต่อไปนี้

ภาพหน้าจอของเทอร์มินัล Google Cloud Shell ที่แสดงว่าสภาพแวดล้อมเชื่อมต่อแล้ว

เครื่องเสมือนนี้มาพร้อมเครื่องมือพัฒนาซอฟต์แวร์ทั้งหมดที่คุณต้องการ โดยมีไดเรกทอรีหลักแบบถาวรขนาด 5 GB และทำงานบน Google Cloud ซึ่งช่วยเพิ่มประสิทธิภาพเครือข่ายและการตรวจสอบสิทธิ์ได้อย่างมาก คุณสามารถทำงานทั้งหมดใน Codelab นี้ได้ภายในเบราว์เซอร์ คุณไม่จำเป็นต้องติดตั้งอะไร

3. ตั้งค่าสภาพแวดล้อมด้วย Terraform

คุณต้องมีสิทธิ์เข้าถึง TPU เพื่อทำแล็บนี้ เวอร์ชันที่ใช้คือ TPU v6e

  • คุณควรทำตามเอกสารแผน TPU และเปิดใช้โควต้า TPU เพื่อรับสิทธิ์เข้าถึง
  • เราใช้การติดตั้งใช้งานขนาดเล็กที่ต้องใช้ชิป TPU v6e 4 ตัว (ct6e-standard-4t)ซึ่งจะเป็นสไลซ์ 2x2 ใน 2 ภูมิภาคที่แตกต่างกัน
  • โทเค็น Hugging Face: ต้องใช้โทเค็นเพื่อการเข้าถึงเพื่อดาวน์โหลดน้ำหนักของโมเดล Gemma

เราจะสร้าง VPC ที่กำหนดเองพร้อมกฎไฟร์วอลล์ พื้นที่เก็บข้อมูล และซับเน็ต เปิด Cloud Console แล้วเลือกโปรเจ็กต์ที่จะใช้

  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 ไฟล์จะเพิ่มชื่อโปรเจ็กต์ ภูมิภาค และข้อมูลโซน ป.ล. อัปเดตตัวแปร "regions" default = ["europe-west4", "us-east5"] ด้วยภูมิภาคที่คุณมีโควต้า TPU ดูข้อมูลเพิ่มเติมได้ที่เอกสาร "ตรวจสอบความพร้อมใช้งานของ TPU ใน GKE"

network.tf จะเพิ่ม VPC ใหม่ในโปรเจ็กต์ของคุณพร้อมซับเน็ตใน 2 โซนที่แตกต่างกัน ซับเน็ตเฉพาะพร็อกซี และกฎไฟร์วอลล์

provider.tf เพิ่มผู้ให้บริการที่เกี่ยวข้องเพื่อรองรับ Terraform

fuse.tf จะเพิ่ม Bucket ของ Cloud Storage เพื่อแคชน้ำหนักของโมเดลและจัดสรรบัญชีบริการ IAM ที่มีสิทธิ์ objectAdmin ซึ่งจะเชื่อมโยงบัญชีนี้กับ Workload Identity ของ GKE

  1. ตรวจสอบว่าคุณอยู่ในไดเรกทอรี gke-tf แล้วเรียกใช้คำสั่งต่อไปนี้
    terraform init - เริ่มต้นไดเรกทอรีการทำงาน ขั้นตอนนี้จะดาวน์โหลดผู้ให้บริการที่จำเป็นสำหรับการกำหนดค่าที่ระบุ terraform plan - สร้างแผนการดำเนินการ ซึ่งแสดงการดำเนินการที่ Terraform จะใช้เพื่อทำให้โครงสร้างพื้นฐานใช้งานได้ terraform apply –auto-approve จะเรียกใช้การอัปเดตและอนุมัติโดยอัตโนมัติ
terraform init 
terraform plan 
  1. ตอนนี้ให้เรียกใช้การติดตั้งใช้งาน (การดำเนินการนี้อาจใช้เวลา 3-5 นาที)
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 เพิ่มคลัสเตอร์ 2 รายการในภูมิภาคต่างๆ และสร้าง TPU Nodepool 2 รายการที่เรียกใช้ TPU v6e ที่มีชิป 4 ตัว และกำหนด DRANET ที่มีการจัดการให้กับ Node Pool

  1. ตอนนี้ให้เรียกใช้การติดตั้งใช้งาน (การดำเนินการนี้อาจใช้เวลา 10-15 นาที)
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

4. การจดทะเบียนกลุ่ม

เราต้องลงทะเบียนคลัสเตอร์กับฟลีต

  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 ไฟล์จะลงทะเบียนทั้ง 2 คลัสเตอร์กับ GKE Fleet ทั่วโลก และเปิดใช้ Service Discovery และ Ingress แบบหลายคลัสเตอร์ โดยจะกำหนดให้คลัสเตอร์ในสหรัฐอเมริกาเป็นคลัสเตอร์การกำหนดค่าส่วนกลาง ซึ่งจะช่วยให้ Gateway API ตรวจสอบและกำหนดเส้นทางการรับส่งข้อมูลได้

  1. ในโฟลเดอร์ gke-tf แล้วเรียกใช้ (การดำเนินการนี้อาจใช้เวลา 3-5 นาที)
terraform plan 
terraform apply -auto-approve
  1. ตรวจสอบการจดทะเบียนกองยานพาหนะ
gcloud container fleet memberships list --project=$PROJECT_ID

5. แคชน้ำหนักโมเดลไปยัง FUSE

เราจะเรียกใช้ Kubernetes Job ชั่วคราวในคลัสเตอร์ของสหรัฐอเมริกาเพื่อดาวน์โหลดโมเดล Gemma อย่างปลอดภัยผ่านสคริปต์ Python ลงในที่เก็บข้อมูล Cloud Storage ที่ติดตั้ง 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. รอให้การดาวน์โหลดเสร็จสิ้นก่อนดำเนินการต่อ (ขั้นตอนนี้ควรใช้เวลา 5-10 นาที ขึ้นอยู่กับขนาดโมเดล)
kubectl logs -f job/model-downloader --context=$CTX_US

(กด Ctrl+C เพื่อออกจากบันทึกเมื่อระบบแจ้งว่า "ดาวน์โหลดเสร็จสมบูรณ์")

6. ทำให้ภาระงาน 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. ตอนนี้ให้เรียกใช้สคริปต์ต่อไปนี้ (การดำเนินการนี้จะใช้เวลา 5-10 นาทีเนื่องจากมีการติดตั้งใช้งานใน 2 ภูมิภาค)
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 สำหรับการเชื่อมต่อเครือข่ายพ็อดมาตรฐาน พร้อมกับอินเทอร์เฟซรองที่แสดงถึง Fabric ของ TPU เฉพาะ eth1, eth2 ฯลฯ

7. การกำหนดค่า Inference 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

8. การกำหนดค่าเกตเวย์

ตอนนี้คุณจะสร้างการกำหนดค่าเกตเวย์ข้ามภูมิภาค Gateway(cross-region-gateway), HTTPRoute (gemma-route), HealthCheckPolicy(gemma-health-check)and GCPBackendPolicy(gemma-backend-policy. พูลการอนุมานสร้างขึ้นโดยใช้แผนภูมิ Helm การติดตั้งและตรวจสอบการสร้าง (ระบบจะใช้เวลา 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): จัดกลุ่มเซิร์ฟเวอร์โมเดลจากทั้ง 2 ภูมิภาคเป็นแบ็กเอนด์เชิงตรรกะเดียว

Gateway และ HTTPRoute: สร้างตัวจัดสรรภาระงานภายในส่วนกลางจริงและกำหนดกฎสำหรับการกำหนดเส้นทางพรอมต์ AI ขาเข้าไปยังโมเดล

นโยบายการตรวจสอบสถานะและแบ็กเอนด์: ตรวจสอบว่าได้ส่งคำขอไปยังพ็อดที่ทำงานอยู่เท่านั้น และเปิดใช้การกระจายการเข้าชมอัจฉริยะตามเมตริก (ป้องกันไม่ให้ TPU ทำงานหนักเกินไป)

การตรวจสอบความถูกต้อง: สคริปต์จะหยุดชั่วคราวเพื่อให้แน่ใจว่า Google Cloud จัดสรรที่อยู่ IP ภายในอย่างเต็มรูปแบบก่อนที่คุณจะดำเนินการต่อ

9. การทดสอบเฟลโอเวอร์

มาถึงส่วนที่ดีที่สุดของแล็บแล้ว นั่นคือการทดสอบความพร้อมใช้งานสูงของสถาปัตยกรรม

การทดสอบอัตโนมัตินี้จะทำสิ่งต่อไปนี้

  • การทดสอบพื้นฐาน: ผู้ใช้จำลองของเราจะส่งพรอมต์การอนุมาน ("เมืองหลวงของฝรั่งเศสคืออะไร") เนื่องจากผู้ใช้อยู่ในภูมิภาคหลัก เกตเวย์จึงกำหนดเส้นทางการส่งคำขอไปยัง TPU ในพื้นที่เหล่านั้นเพื่อให้เกิดเวลาในการตอบสนองที่ต่ำที่สุด
  • เหตุการณ์ภัยพิบัติ: เราจำลองการหยุดทำงานของศูนย์ข้อมูลที่ร้ายแรงโดยการปิดพ็อด TPU ทั้งหมดในภูมิภาคหลัก (replicas=0)
  • การตรวจหา: เรารอ 45 วินาที ในระหว่างช่วงเวลานี้ การตรวจสอบสถานะของเกตเวย์จะล้มเหลว เกตเวย์จะทราบว่าแบ็กเอนด์หลักออฟไลน์โดยสมบูรณ์ และจะอัปเดตตารางการกำหนดเส้นทางส่วนกลางแบบไดนามิก
  • เฟลโอเวอร์: ผู้ใช้ส่งพรอมต์ที่ 2 ("เมืองหลวงของเยอรมนีคืออะไร") ผู้ใช้ไม่ทราบว่าเกิดการหยุดทำงาน เกตเวย์จะสกัดกั้นคำขอและเปลี่ยนเส้นทางคำขอไปยัง 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. เมื่อการทดสอบเสร็จสมบูรณ์แล้ว คุณสามารถล้างข้อมูลได้

10. ล้างข้อมูล

  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อีกครั้ง

11. ขอแสดงความยินดี

ยินดีด้วย คุณได้ติดตั้งใช้งาน GKE Inference Gateway แบบหลายคลัสเตอร์ที่มีความพร้อมใช้งานสูง สถาปัตยกรรมการอนุมาน AI แบบข้ามภูมิภาคโดยใช้ GKE, DRANET ที่มีการจัดการ และตัวเร่ง TPU v6e เรียบร้อยแล้ว

การผสานรวม Cloud Storage FUSE เพื่อการโหลดโมเดลทันทีและ Inference Gateway API เพื่อการกำหนดเส้นทางแบบหลายคลัสเตอร์ที่คำนึงถึงเวลาในการตอบสนองจะช่วยให้คุณสร้างแบ็กเอนด์ที่ยืดหยุ่นซึ่งสามารถรับมือกับการหยุดทำงานของศูนย์ข้อมูลระดับภูมิภาคได้อย่างสมบูรณ์โดยไม่ทำให้การรับส่งข้อมูลของผู้ใช้ภายในลดลง

ขั้นตอนถัดไป / ดูข้อมูลเพิ่มเติม

อ่านข้อมูลเพิ่มเติมเกี่ยวกับเครือข่าย GKE

เข้าร่วมแล็บถัดไป

ทำภารกิจต่อด้วย Google Cloud และดูแล็บอื่นๆ ของ Google Cloud เหล่านี้