1. مقدمة
في هذا الدرس التطبيقي حول الترميز، ستتعرّف على كيفية نشر خدمات استنتاج vLLM (نموذج لغوي كبير افتراضي) عالي الأداء ومتعدد المضيفين على Google Kubernetes Engine (GKE) باستخدام وحدات TPU من Google Cloud. ستضبط إعدادات الاستنتاج الموزّع باستخدام Ray وتدير عبء العمل بشكلٍ أصلي على GKE باستخدام LeaderWorkerSets.
يحاكي هذا الدليل الإرشادي عملية إعداد الإنتاج لعرض نماذج كبيرة مثل Qwen 30B.
الإجراءات التي ستنفذّها
- أنشئ شبكة VPC مخصّصة لحركة بيانات أداة تسريع الألعاب.
- توفير مجموعة GKE مع Ray Operator وبرنامج تشغيل GCS Fuse CSI.
- ابدأ بإنشاء ذاكرة تخزين مؤقت سريعة في GCS لتحميل النماذج بشكل أسرع.
- توفير مجموعة أجهزة ذات التخصيص نفسه لوحدة معالجة الموتّرات v6e متعددة المضيفين مع سعة محجوزة
- إعداد Workload Identity للوصول الآمن إلى أوزان النماذج
- تفعيل محرك vLLM واختباره لعرض نموذج مَعلمات يبلغ حجمه 30 مليارًا

المتطلبات
- مشروع Google Cloud تم تفعيل الفوترة فيه
- حجز على Google Cloud لموارد TPU v6e (32 شريحة،
ct6e-standard-4t) - إذن الوصول إلى نُسخ أوزان النماذج من حزمة مصدر
- Cloud Shell أو وحدة طرفية محلية مثبَّت عليها
gcloudوkubectlوhelm
- المدة المقدَّرة: 60 دقيقة
- التكلفة المقدّرة: أقل من 60 دولارًا أمريكيًا (بافتراض أنّ عملية التفكيك ستتمّ على الفور).
2. قبل البدء
إنشاء مشروع Google Cloud أو اختياره
- في Google Cloud Console، اختَر مشروعًا على Google Cloud أو أنشِئ مشروعًا.
- تأكَّد من تفعيل الفوترة لمشروعك على Cloud.
بدء Cloud Shell
- انقر على تفعيل Cloud Shell في أعلى "وحدة تحكّم Google Cloud".
- إثبات صحة المصادقة:
gcloud auth list
- أكِّد مشروعك:
gcloud config get project
- اضبطه إذا لزم الأمر:
export PROJECT_ID=<YOUR_PROJECT_ID>
gcloud config set project $PROJECT_ID
ضبط متغيرات البيئة
لتسهيل تنفيذ الأوامر، حدِّد المتغيّرات التالية في shell. استبدِل <YOUR_ZONE> بمنطقة TPU المخصّصة لك و <YOUR_RESERVATION_NAME> بمعرّف الحجز. عليك إنشاء رمز دخول مستخدم Hugging Face لتنزيل أوزان النماذج المحظورة. بعد إنشائه، استبدِل <YOUR_HUGGING_FACE_TOKEN> بالرمز المميز الذي أنشأته حديثًا.
export PROJECT_ID=$(gcloud config get-value project)
export PROJECT_NUMBER=$(gcloud projects describe ${PROJECT_ID} --format="value(projectNumber)")
export ZONE="<YOUR_ZONE>" # e.g., us-east5-a
export REGION=${ZONE%-*}
export CLUSTER_NAME="qwen-serving-cluster"
export GVNIC_NETWORK_PREFIX="qwen-serving"
export BUCKET_NAME="inf-demo-model-storage-${PROJECT_NUMBER}"
export RESERVATION_NAME="<YOUR_RESERVATION_NAME>"
export NODE_POOL_NAME="tpu-v6e-32-resvd-pool"
export MULTIHOST_COLLECTION_NAME="tpu-6-collection"
export HF_TOKEN="<YOUR_HUGGING_FACE_TOKEN>" # Token with access to Qwen model if restricted
تفعيل واجهات برمجة التطبيقات
فعِّل خدمات Google Cloud المطلوبة:
gcloud services enable \
container.googleapis.com \
compute.googleapis.com \
iam.googleapis.com \
cloudresourcemanager.googleapis.com
3- إنشاء شبكة مخصّصة
تتطلّب أحمال عمل وحدات TPU المتعددة المضيفين إعدادات شبكة معيّنة، بما في ذلك أحجام MTU أكبر لتحقيق كفاءة في التواصل بين أدوات التسريع. أنشئ شبكة VPC مخصّصة لمجموعتك.
- أنشئ شبكة VPC باستخدام وحدة نقل قصوى (MTU) كبيرة (8896):
gcloud compute --project=${PROJECT_ID} \ networks create ${GVNIC_NETWORK_PREFIX}-main \ --subnet-mode=custom \ --mtu=8896 - أنشئ الشبكة الفرعية للمجموعة:
gcloud compute --project=${PROJECT_ID} \ networks subnets create ${GVNIC_NETWORK_PREFIX}-tpu \ --network=${GVNIC_NETWORK_PREFIX}-main \ --region=${REGION} \ --range=192.168.100.0/24 - أنشئ قواعد جدار الحماية التي تسمح بالزيارات الداخلية لتمكين العاملين من التواصل:
gcloud compute --project=${PROJECT_ID} firewall-rules create ${GVNIC_NETWORK_PREFIX}-allow-internal \ --network=${GVNIC_NETWORK_PREFIX}-main \ --allow=all \ --source-ranges=172.16.0.0/12,192.168.0.0/16,10.0.0.0/8 \ --description="Allow all internal traffic within the network."
4. توفير مجموعة GKE
أنشئ إعدادًا عاديًا لمجموعة GKE تم ضبطه ليتوافق مع عمليات ربط GCS Fuse وأحمال عمل Ray Operator.
- إنشاء المجموعة:
gcloud container clusters create ${CLUSTER_NAME} \ --project=${PROJECT_ID} \ --location=${REGION} \ --release-channel=rapid \ --machine-type=e2-standard-4 \ --network=${GVNIC_NETWORK_PREFIX}-main \ --subnetwork=${GVNIC_NETWORK_PREFIX}-tpu \ --num-nodes=1 \ --gateway-api=standard \ --enable-managed-prometheus \ --enable-dataplane-v2 \ --enable-dataplane-v2-metrics \ --workload-pool=${PROJECT_ID}.svc.id.goog \ --addons=GcsFuseCsiDriver,RayOperator \ --enable-ip-alias - استرداد بيانات اعتماد المجموعة:
gcloud container clusters get-credentials ${CLUSTER_NAME} --region=${REGION} - إنشاء رمز Hugging Face السرّي: احفظ الرمز المميز بشكل آمن لتنزيل الحاويات:
kubectl create secret generic hf-secret \ --from-literal=hf_api_token=${HF_TOKEN} \ --dry-run=client -o yaml | kubectl apply -f - - ثبِّت LeaderWorkerSet (LWS) باستخدام Helm. تتولّى خدمة LWS إدارة مجموعات من وحدات البود التي يجب جدولة عملياتها معًا:
helm install lws oci://registry.k8s.io/lws/charts/lws \ --version=0.7.0 \ --namespace lws-system \ --create-namespace \ --wait
5- تفعيل ميزة "ذاكرة التخزين المؤقت السريع" في GCS
لتسريع قراءة عشرات غيغابايت من الأوزان من Cloud Storage أثناء العرض، أنشئ حزمة GCS وفعِّل GCS Rapid Cache في منطقتك.
- إنشاء الحزمة:
gcloud storage buckets create gs://$BUCKET_NAME \ --location=$REGION \ --uniform-bucket-level-access - إعداد Rapid Cache في منطقة TPU:
gcloud storage buckets anywhere-caches create gs://$BUCKET_NAME $ZONE \ --ttl=1d \ --admission-policy=ADMIT_ON_FIRST_MISS
6. إعداد Workload Identity وأذونات التخزين
اضبط روابط التعريف لتركيب مجموعة البيانات الخاصة بالوزن بشكل آمن في وحدات GKE بدون تضمين مفاتيح طويلة الأمد.
- إنشاء حساب خدمة مخصّص في "إدارة الهوية وإمكانية الوصول":
gcloud iam service-accounts create tpu-reader-sa - منح أذونات قراءة الحزمة:
gcloud storage buckets add-iam-policy-binding gs://${BUCKET_NAME} \ --member="serviceAccount:tpu-reader-sa@${PROJECT_ID}.iam.gserviceaccount.com" \ --role="roles/storage.objectAdmin" - أنشئ ربط Workload Identity لحساب خدمة Kubernetes في مساحة الاسم
default:gcloud iam service-accounts add-iam-policy-binding tpu-reader-sa@${PROJECT_ID}.iam.gserviceaccount.com \ --role="roles/iam.workloadIdentityUser" \ --member="serviceAccount:${PROJECT_ID}.svc.id.goog[default/default]" - إضافة تعليق توضيحي إلى حساب الخدمة في Kubernetes:
kubectl annotate serviceaccount default iam.gke.io/gcp-service-account=tpu-reader-sa@${PROJECT_ID}.iam.gserviceaccount.com
7. إعداد أوزان النماذج
لعرض نموذج مَعلمات 30B، عليك تنزيل الأوزان من Hugging Face إلى حزمة GCS. لتجاوز الحد الأقصى المسموح به لحصة القرص في Cloud Shell (5 غيغابايت)، استخدِم مهمة Standard Kubernetes لتنزيل البيانات مباشرةً داخل المجموعة والكتابة في وحدة تخزين GCS Fuse المثبَّتة بشكل آمن.
- نشر مهمة "تنزيل النموذج": أنشئ البيان التالي وطبِّقه لبدء عملية التنزيل:
cat <<EOF | kubectl apply -f - apiVersion: batch/v1 kind: Job metadata: name: model-downloader spec: ttlSecondsAfterFinished: 60 template: metadata: annotations: gke-gcsfuse/volumes: "true" gke-gcsfuse/memory-limit: "0" spec: serviceAccountName: default restartPolicy: OnFailure containers: - name: downloader image: python:3.10-slim command: ["/bin/sh", "-c"] args: - | pip install -U "huggingface_hub[hf_transfer]" filelock export HF_HUB_ENABLE_HF_TRANSFER=1 python -c ' import filelock class DummyLock: def __init__(self, *args, **kwargs): pass def __enter__(self): return self def __exit__(self, *args): pass def acquire(self, *args, **kwargs): pass def release(self, *args, **kwargs): pass filelock.FileLock = DummyLock from huggingface_hub import snapshot_download snapshot_download( repo_id="Qwen/Qwen3-30B-A3B", local_dir="/models/qwen3-30b-weights", local_dir_use_symlinks=False ) ' env: - name: HF_TOKEN valueFrom: secretKeyRef: name: hf-secret key: hf_api_token volumeMounts: - name: model-weights mountPath: /models volumes: - name: model-weights csi: driver: gcsfuse.csi.storage.gke.io volumeAttributes: bucketName: ${BUCKET_NAME} mountOptions: "implicit-dirs" EOF - مراقبة عملية التنزيل: راجِع سجلّات وحدة تنزيل البيانات (downloader pod) لتتبُّع مستوى التقدّم:
انتظِر إلى أن تكتمل المهمة بنجاح.kubectl logs -f job/model-downloader
8. إنشاء مجموعة عقد لوحدة معالجة الموتّرات محجوزة
وفِّر شريحة TPU الفعلية التي تتضمّن عدة مضيفين باستخدام حجز السعة الحالي.
- نفِّذ أمر الإنشاء:
gcloud beta container node-pools create ${NODE_POOL_NAME} \ --project=${PROJECT_ID} \ --cluster=${CLUSTER_NAME} \ --region=${REGION} \ --node-locations=${ZONE} \ --machine-type=ct6e-standard-4t \ --tpu-topology=4x8 \ --num-nodes=8 \ --scopes=https://www.googleapis.com/auth/cloud-platform \ --reservation-affinity=specific \ --reservation=${RESERVATION_NAME} \ --accelerator-network-profile=auto \ --node-labels=cloud.google.com/gke-nodepool-group-name=${MULTIHOST_COLLECTION_NAME} \ --node-labels=cloud.google.com/gke-workload-type=HIGH_AVAILABILITY \ --node-labels=cloud.google.com/gke-networking-dra-driver=true - انتظار انضمام العُقد: يمكنك مراقبة توسيع نطاق تجميع العُقد مباشرةً. انتظِر إلى أن تنضم 8 عُقد تحتوي على
ct6eإلىkubectl get nodes.
9- نشر خدمة vLLM
- إنشاء مطالبات الشبكة: عليك طلب بيئة الشبكة:
cat <<EOF | kubectl apply -f - apiVersion: resource.k8s.io/v1 kind: ResourceClaimTemplate metadata: name: all-netdev spec: spec: devices: requests: - name: req-netdev exactly: deviceClassName: netdev.google.com allocationMode: All EOF - نشر نقطة نهاية واجهة برمجة التطبيقات الخاصة بجهاز موازنة الحمل:
cat <<EOF | kubectl apply -f - apiVersion: v1 kind: Service metadata: name: vllm-tpu-service spec: type: LoadBalancer selector: leaderworkerset.sigs.k8s.io/name: vllm-tpu-qwen leaderworkerset.sigs.k8s.io/worker-index: "0" ports: - protocol: TCP port: 8000 targetPort: 8000 EOF - نشر عبء عمل LeaderWorkerSet: يبدأ ملف البيان هذا تجميع خادم Ray الرئيسي/العامل بشكل ديناميكي على مستوى 8 مضيفين للشرائح.
cat <<EOF | kubectl apply -f - apiVersion: leaderworkerset.x-k8s.io/v1 kind: LeaderWorkerSet metadata: name: vllm-tpu-qwen spec: replicas: 1 leaderWorkerTemplate: size: 8 restartPolicy: RecreateGroupOnPodRestart workerTemplate: metadata: annotations: gke-gcsfuse/volumes: "true" gke-gcsfuse/memory-limit: "0" labels: leaderworkerset.sigs.k8s.io/name: vllm-tpu-qwen gke-gcsfuse/volumes: "true" spec: hostname: vllm-tpu-qwen serviceAccountName: default containers: - name: vllm-tpu image: vllm/vllm-tpu:nightly command: ["sh", "-c"] args: - | MY_TPU_IP=\$(hostname -I | awk '{print \$1}') echo "My TPU Network IP is: \$MY_TPU_IP" LEADER_DNS="vllm-tpu-qwen-0.vllm-tpu-qwen" until getent hosts \$LEADER_DNS; do echo "DNS not ready. Sleeping 5s..." sleep 5 done LEADER_IP=\$(getent hosts \$LEADER_DNS | awk '{print \$1}') export JAX_PLATFORMS='' export SCAN_TPU_CHIPS=True export TPU_MULTIHOST_BACKEND=ray export JAX_DISTRIBUTED_INITIALIZATION_TIMEOUT=300 export LD_LIBRARY_PATH=\$LD_LIBRARY_PATH:/usr/local/lib export VLLM_HOST_IP=\$MY_TPU_IP if [ "\$LWS_WORKER_INDEX" = "0" ]; then echo "Starting Ray Head..." ray start --head --port=6379 --node-ip-address=\$MY_TPU_IP --resources='{"TPU": 4}' --block & sleep 20 until ray status; do sleep 5; done echo "Starting vLLM API Server..." python3 -m vllm.entrypoints.openai.api_server \ --model=/models/qwen3-30b-weights \ --tensor-parallel-size=32 \ --pipeline-parallel-size=1 \ --distributed-executor-backend=ray \ --host=0.0.0.0 --port=8000 \ --enforce-eager \ --gpu-memory-utilization=0.90 else ray start --address=\${LEADER_IP}:6379 --node-ip-address=\$MY_TPU_IP --resources='{"TPU": 4}' --block fi ports: - containerPort: 8000 - containerPort: 6379 volumeMounts: - name: model-weights mountPath: /models readOnly: true - name: dshm mountPath: /dev/shm resources: claims: - name: net-resources limits: google.com/tpu: 4 memory: "100Gi" requests: google.com/tpu: 4 memory: "100Gi" nodeSelector: cloud.google.com/gke-tpu-accelerator: tpu-v6e-slice cloud.google.com/gke-tpu-topology: 4x8 gke.networks.io/accelerator-network-profile: auto resourceClaims: - name: net-resources resourceClaimTemplateName: all-netdev volumes: - name: model-weights csi: driver: gcsfuse.csi.storage.gke.io volumeAttributes: bucketName: ${BUCKET_NAME} mountOptions: "implicit-dirs" - name: dshm emptyDir: medium: Memory EOF
10. استجابة اختبار النشر
قد يستغرق الأمر من 5 إلى 10 دقائق حتى تسحب جميع الحاويات في LeaderWorkerSet صور الحاويات، وتهيئ Ray، وتصبح Ready بالكامل. يمكنك تتبُّع الحالة من خلال مراقبة عملية تهيئة الحزمة:
kubectl get pods -l leaderworkerset.sigs.k8s.io/name=vllm-tpu-qwen -w
انتظِر إلى أن تعرض جميع وحدات vllm-tpu-qwen- STATUS الثماني Running وREADY 2/2، وتأكَّد من أنّ موازن التحميل قد تلقّى عنوان IP خارجيًا قبل المتابعة. قد تستغرق هذه العملية من 7 إلى 10 دقائق.
- استرداد عنوان IP الخارجي:
export EXTERNAL_IP=$(kubectl get svc vllm-tpu-service -o jsonpath='{.status.loadBalancer.ingress[0].ip}') echo $EXTERNAL_IP
تنبيه: في خدمة إنتاجية، يجب تأمين نقطة النهاية هذه باستخدام خدمة مثل Identity Aware Proxy (IAP).
- إرسال طلب استنتاج باستخدام
curl: من المفترض أن يظهر لك ناتج يشبه استجابة JSON تحتوي على النص الذي يمثّل الاستنتاج الذي تم إنشاؤه.curl -N -s http://$EXTERNAL_IP:8000/v1/chat/completions \ -H "Content-Type: application/json" \ -d '{ "model": "/models/qwen3-30b-weights", "messages": [{"role": "user", "content": "Write a haiku about high-performance computing on TPUs."}], "temperature": 0.7, "max_tokens": 100, "stream": true }' | sed 's/^data: //' | grep -v '\[DONE\]' | grep -v '^$' | jq -rj '.choices[0].delta.content // empty' ; echo ""
11. تَنظيم
لتجنُّب الرسوم المستمرة على حسابك على Google Cloud، احذف الموارد التي تم إنشاؤها أثناء هذا الدرس العملي.
- حذف مجموعة العُقد:
gcloud container node-pools delete "${NODE_POOL_NAME}" \ --cluster="${CLUSTER_NAME}" \ --region="${REGION}" \ --project="${PROJECT_ID}" --quiet - حذف المجموعة:
gcloud container clusters delete "${CLUSTER_NAME}" \ --region="${REGION}" \ --project="${PROJECT_ID}" --quiet - حذف إعدادات الشبكة وجدار الحماية:
gcloud compute firewall-rules delete \ "${GVNIC_NETWORK_PREFIX}-allow-internal" \ --project="${PROJECT_ID}" --quiet gcloud compute networks subnets delete "${GVNIC_NETWORK_PREFIX}-tpu" \ --region="${REGION}" --quiet gcloud compute networks delete "${GVNIC_NETWORK_PREFIX}-main" --quiet - إلغاء ربط حساب الخدمة وحذفه:
# 1. Create the cleanup script cat << 'EOF' > clean_up_sa.sh #!/bin/bash # Validate that PROJECT_ID is available if [ -z "$PROJECT_ID" ]; then echo "Error: PROJECT_ID environment variable is not set." exit 1 fi SA_EMAIL="tpu-reader-sa@${PROJECT_ID}.iam.gserviceaccount.com" SA_MEMBER="serviceAccount:${SA_EMAIL}" echo "Gathering IAM policy for ${SA_EMAIL}..." # Fetch roles assigned to this specific SA ROLES=$(gcloud projects get-iam-policy ${PROJECT_ID} \ --flatten="bindings[].members" \ --filter="bindings.members:${SA_MEMBER}" \ --format="value(bindings.role)") if [ -z "$ROLES" ]; then echo "No IAM bindings found for this service account." else for ROLE in $ROLES; do echo "Removing binding for: ${ROLE}..." gcloud projects remove-iam-policy-binding ${PROJECT_ID} \ --member="${SA_MEMBER}" \ --role="${ROLE}" --quiet > /dev/null done echo "Successfully unbound all roles." fi # 2. Delete the service account itself echo "Deleting service account..." gcloud iam service-accounts delete ${SA_EMAIL} --project=${PROJECT_ID} --quiet echo "Cleanup complete." EOF # 2. Make the script executable and run it chmod +x clean_up_sa.sh ./clean_up_sa.sh - حذف حزمة GCS انتقِل إلى Cloud Console، واختَر Cloud Storage -> الحِزم، ثمّ اختَر inf-demo-model-storage، وبعد ذلك اختَر "حذف".
12. تهانينا
تهانينا! لقد نشرت بنجاح حزمة vLLM متعددة المضيفين وعالية معدّل الاستدلال باستخدام Ray بشكلٍ أصلي على Google Kubernetes Engine.
ما تعلّمته
- توفير مسارات مخصّصة مصمّمة لنقل بيانات TPU بسرعة عالية
- تحديد أوزان التركيب باستخدام GCS Fuse وذاكرات التخزين المؤقت الإقليمية السريعة
- تنظيم شرائح أحمال العمل المتعددة المضيفين التي تتم مزامنتها تلقائيًا من خلال LeaderWorkerSets
- لمزيد من المعلومات، يُرجى الاطّلاع على دليل مستخدم vLLM وأدلة نشر llm-d.