1. סקירה כללית
בשיעור ה-Lab הזה נכיר את ClusterNetworkPolicy ב-Google Kubernetes Engine (GKE). אפשר להחיל את ClusterNetworkPolicy על תרחישים שונים לדוגמה, וגם על כל האשכול, ולאכוף אותה ברמות Admin ו-Baseline. בדוגמה הזו נדגים את הבקרה באמצעות פריסת LLM (Gemma) ב-GKE עם TPU, vLLM ואימות.
תעבדו עם הטכנולוגיות הבאות:
- Google Kubernetes Engine (GKE) – פלטפורמת הבסיס לתזמור קונטיינרים, שמארחת עומסי עבודה פרטיים וניתנים להתאמה.
- ClusterNetworkPolicy (CNP) – כללי מדיניות רשת ב-Kubernetes בכמה רמות בהיקף של אשכול, שמאפשרים להגדיר אמצעי הגנה של SecOps ברמות Admin, NetworkPolicy ו-Baseline.
- vLLM TPU Inference Engine – מנוע להסקת מסקנות עם תפוקה גבוהה שמריץ את המודל הקל Gemma 4 (google/gemma-4-E2B-it) ב-Google TPU עם נקודות קצה (endpoints) של REST שתואמות ל-OpenAI.
תפקידים ופרסונות
Network Policy API בנוי סביב פרסונות ארגוניות ספציפיות:
- שימוש באדמין של אשכול (
ClusterNetworkPolicy): אכיפת אמצעי אבטחה באשכולות ברמות אדמין וברמות בסיסיות. - שימוש באדמין של מרחב שמות (
NetworkPolicy): ניהול הגבולות והבידוד ברמת הדייר במרחבי שמות ספציפיים. - שימוש על ידי מפתחי אפליקציות (
NetworkPolicy): הגדרת כללים מפורטים למיקרו-שירותים (microservices) ספציפיים לאפליקציה.
כדי להגדיר את הסביבה הזו, תפרסו VPC פרטי בהתאמה אישית, Cloud NAT ואשכול GKE פרטי עם מאגר צמתים ייעודי של TPU.
כדי לאבטח את האשכול, תיצרו משאבים מסוג ClusterNetworkPolicy בכמה רמות לצד אובייקטים רגילים מסוג NetworkPolicy, כדי לאכוף אמצעי הגנה קפדניים ברשת. לאחר מכן תפרסו עומס עבודה של AI שמשתמש ב-vLLM וב-Google Cloud TPU כדי להציג את המודל Gemma 4.
לבסוף, תגדירו בסיס עבודה, תפעילו את כללי מדיניות האבטחה ותגדירו את NetworkLogging באמצעות Cloud Logging וסטרימינג בזמן אמת של Hubble eBPF observability כדי לבדוק את ההחלטות לגבי המדיניות ולאמת את חבילות הנתונים שנמחקו. כל ההגדרות ישתמשו בשילוב של Terraform, gcloud ו-kubectl.
בשיעור ה-Lab הזה תלמדו איך לבצע את המשימה הבאה:
- הקצאת רשת פרטית, אשכול GKE ומאיצי Cloud TPU v5e באמצעות Terraform.
- מגדירים את GKE NetworkLogging כדי לתעד את נתוני הטלמטריה של החיבור.
- פריסת Gemma 4 E2B ב-TPU באמצעות קובץ אימג' של קונטיינר vllm/vllm-tpu הרשמי.
- לפני שמחילים הגבלות, צריך להגדיר קו בסיס תקין שמוכיח קישוריות מקצה לקצה.
- מחילים
Baseline-tierClusterNetworkPolicy כדי לדחות כברירת מחדל תנועה לא מורשית בין מרחבי שמות. - להחיל מדיניות NetworkPolicy שמנוהלת על ידי המפתח כדי להעביר את הגישה ללקוחות מורשים.
- מחילים מדיניות ClusterNetworkPolicy של
Admin-tierכדי לאכוף כללי הגנה גלובליים לתעבורת נתונים יוצאת, שמבטלים את הגדרות המפתחים. - אימות של חבילות נתונים שהועברו או נמחקו בזמן אמת (באמצעות סטרימינג של Hubble eBPF ו-Cloud Logging).
בשיעור ה-Lab הזה תיצרו את התבנית הבאה.
איור 1. 
2. הגדרת שירותי Google Cloud
הגדרת סביבה בקצב אישי
- נכנסים למסוף Google Cloud ויוצרים פרויקט חדש או משתמשים בפרויקט קיים. אם עדיין אין לכם חשבון Gmail או Google Workspace, אתם צריכים ליצור חשבון.



- שם הפרויקט הוא השם המוצג למשתתפים בפרויקט הזה. זו מחרוזת תווים שלא משמשת את Google APIs. תמיד אפשר לעדכן אותו.
- מזהה הפרויקט הוא ייחודי לכל הפרויקטים ב-Google Cloud, ואי אפשר לשנות אותו אחרי שהוא הוגדר. מסוף Cloud יוצר באופן אוטומטי מחרוזת ייחודית, שבדרך כלל לא משנה מה היא. ברוב ה-Codelabs תצטרכו להפנות למזהה הפרויקט (בדרך כלל מזוהה בתור
PROJECT_ID). אם אתם לא אוהבים את המזהה שנוצר, אתם יכולים ליצור מזהה אקראי אחר. לחלופין, אתם יכולים לנסות להזין שם משלכם ולבדוק אם הוא זמין. אי אפשר לשנות אותו אחרי השלב הזה, והוא נשאר לאורך כל הפרויקט. - לידיעתכם, יש ערך שלישי, מספר פרויקט, שמשמש בחלק מממשקי ה-API. מידע נוסף על שלושת הערכים האלה זמין במאמרי העזרה.
- בשלב הבא, צריך להפעיל את החיוב במסוף Cloud כדי להשתמש במשאבים או בממשקי ה-API של Cloud. השימוש ב-Codelab הזה לא יעלה הרבה, אם בכלל. כדי להשבית את המשאבים ולהימנע מחיובים נוספים, אתם יכולים למחוק את המשאבים שיצרתם או למחוק את הפרויקט. משתמשים חדשים ב-Google Cloud זכאים לתקופת ניסיון בחינם בשווי 300$.
הפעלת Cloud Shell
אפשר להפעיל את Google Cloud מרחוק מהמחשב הנייד, אבל ב-Codelab הזה תשתמשו ב-Google Cloud Shell, סביבה של שורת פקודה שפועלת בענן.
במסוף Google Cloud, לוחצים על הסמל של Cloud Shell בסרגל הכלים שבפינה הימנית העליונה:

ההקצאה והחיבור לסביבה אמורים להימשך רק כמה רגעים. בסיום התהליך, אמור להופיע משהו כזה:

המכונה הווירטואלית הזו נטענת עם כל הכלים למפתחים שתצטרכו. יש בה ספריית בית בנפח מתמיד של 5GB והיא פועלת ב-Google Cloud, מה שמשפר מאוד את ביצועי הרשת והאימות. את כל העבודה ב-Codelab הזה אפשר לעשות בדפדפן. לא צריך להתקין שום דבר.
3. הגדרת סביבה באמצעות Terraform
כדי להשלים את ה-Lab,
- צריכה להיות לכם גישה למכסת TPU v5e באזור ובתחום שבחרתם, למשל (
us-central1-a, us-west4-a, us-south1-a, us-west1-c, europe-west4-b). כדי לקבל גישה, צריך לפעול לפי ההוראות במסמך של תוכנית TPU ולהפעיל את מכסת TPU. - זהו פריסה קטנה שדורשת 4 שבבי TPU v5e (
ct5lp-hightpu-4t),) שייצרו פרוסה בגודל 2x2 באזור אחד.
כדי להשתמש ב-Hugging Face
- טוקן של Hugging Face: צריך טוקן גישה כדי להוריד את משקלי המודל של Gemma.
- מאשרים את הסכם השימוש, אם נדרש.
ניצור VPC בהתאמה אישית עם כללים של חומת אש, רשת משנה ואשכול במצב Autopilot. פותחים את מסוף Cloud ובוחרים את הפרויקט שבו רוצים להשתמש.
- פותחים את Cloud Shell בחלק העליון של המסוף בצד שמאל, מוודאים שמופיע מזהה הפרויקט הנכון ב-Cloud Shell ומאשרים את כל ההנחיות כדי לאפשר גישה.

- יוצרים ספרייה בשם
gke-cnp-lab,ועוברים אליה.
mkdir -p gke-cnp-lab && cd gke-cnp-lab
- עכשיו יוצרים כמה משתנים. הערה: עורכים את REGION ואת ZONE כדי לשקף את המיקום בפועל שבו יש לכם גישה למשאבי ה-TPU. ברירת המחדל שמשמשת באזור
us-west4ובאזורus-west4-aשל ה-Lab
export PROJECT_ID=$(gcloud config get-value project)
export REGION="us-west4"
export ZONE="us-west4-a"
export NETWORK="cnp-demo-vpc"
export SUBNET="cnp-main-subnet"
export CLUSTER_NAME="cnp-demo-cluster"
- שומרים את משתני ההגדרות בסקריפט מסייע בספרייה gke-cnp-lab. אם הסשן של Cloud Shell מתנתק או מתאפס, אפשר לשחזר במהירות את המשתנים הפעילים על ידי הפעלת
source ./env.sh
cat << EOF > env.sh
export PROJECT_ID="${PROJECT_ID}"
export REGION="${REGION}"
export ZONE="${ZONE}"
export NETWORK="${NETWORK}"
export SUBNET="${SUBNET}"
export CLUSTER_NAME="${CLUSTER_NAME}"
echo -e "\n=== Validating Environment Configuration ==="
echo "Project ID : \${PROJECT_ID}"
echo "Region : \${REGION}"
echo "Zone : \${ZONE}"
echo "Network Name : \${NETWORK}"
echo "Subnet Name : \${SUBNET}"
echo "Cluster Name : \${CLUSTER_NAME}"
if [[ -z "\${PROJECT_ID}" || -z "\${REGION}" || -z "\${ZONE}" || -z "\${NETWORK}" || -z "\${SUBNET}" || -z "\${CLUSTER_NAME}" ]]; then
echo -e "\n[ERROR] One or more environment variables are missing! Check your configuration."
else
echo -e "\n[SUCCESS] Environment variables successfully populated and validated."
fi
EOF
chmod +x env.sh
source ./env.sh
- עכשיו צריך להוסיף כמה קובצי תצורה. הפקודות האלה ייצרו את הקבצים הבאים: terraform.tfvars , variables.tf, main.tf בספרייה gke-cnp-lab.
cat << EOF > terraform.tfvars
project_id = "${PROJECT_ID}"
region = "${REGION}"
zone = "${ZONE}"
network_name = "${NETWORK}"
subnet_name = "${SUBNET}"
cluster_name = "${CLUSTER_NAME}"
EOF
cat << 'EOF' > variables.tf
variable "project_id" {
type = string
description = "Google Cloud Project ID"
}
variable "region" {
type = string
description = "Target GCP region"
}
variable "zone" {
type = string
description = "Target GCP zone"
}
variable "network_name" {
type = string
description = "VPC network name"
}
variable "subnet_name" {
type = string
description = "Subnetwork name"
}
variable "cluster_name" {
type = string
description = "GKE cluster name"
}
EOF
cat << 'EOF' > main.tf
terraform {
required_version = ">= 1.5.0"
required_providers {
google = {
source = "hashicorp/google"
version = "~> 7.32.0"
}
google-beta = {
source = "hashicorp/google-beta"
version = "~> 7.32.0"
}
}
}
provider "google" {
project = var.project_id
region = var.region
}
provider "google-beta" {
project = var.project_id
region = var.region
}
# 1. Custom Private VPC Network
resource "google_compute_network" "vpc" {
name = var.network_name
auto_create_subnetworks = false
mtu = 8896
}
# 2. Private Subnet with Secondary IP Ranges for Pods & Services
resource "google_compute_subnetwork" "subnet" {
name = var.subnet_name
ip_cidr_range = "10.10.0.0/20"
region = var.region
network = google_compute_network.vpc.id
private_ip_google_access = true
secondary_ip_range {
range_name = "gke-pods"
ip_cidr_range = "10.20.0.0/16"
}
secondary_ip_range {
range_name = "gke-services"
ip_cidr_range = "10.30.0.0/20"
}
}
# 3. Cloud Router & Cloud NAT for Secure Outbound Egress
resource "google_compute_router" "router" {
name = "${var.network_name}-router"
network = google_compute_network.vpc.id
region = var.region
}
resource "google_compute_router_nat" "nat" {
name = "${var.network_name}-nat"
router = google_compute_router.router.name
region = var.region
nat_ip_allocate_option = "AUTO_ONLY"
source_subnetwork_ip_ranges_to_nat = "ALL_SUBNETWORKS_ALL_IP_RANGES"
}
# 4. Internal Firewall Rules
resource "google_compute_firewall" "allow_internal" {
name = "${var.network_name}-allow-internal"
network = google_compute_network.vpc.id
direction = "INGRESS"
priority = 1000
allow {
protocol = "all"
}
source_ranges = ["10.10.0.0/20", "10.20.0.0/16", "10.30.0.0/20"]
}
# 5. Private GKE Cluster with Observability and Dataplane V2
resource "google_container_cluster" "primary" {
name = var.cluster_name
location = var.region
node_locations = [var.zone]
network = google_compute_network.vpc.id
subnetwork = google_compute_subnetwork.subnet.id
initial_node_count = 1
datapath_provider = "ADVANCED_DATAPATH"
# FIX: Observability config must be nested inside monitoring_config
monitoring_config {
advanced_datapath_observability_config {
enable_metrics = true
enable_relay = true
}
}
release_channel {
channel = "RAPID"
}
ip_allocation_policy {
cluster_secondary_range_name = "gke-pods"
services_secondary_range_name = "gke-services"
}
private_cluster_config {
enable_private_nodes = true
enable_private_endpoint = false
master_ipv4_cidr_block = "172.16.0.0/28"
}
node_config {
machine_type = "e2-standard-4"
oauth_scopes = ["https://www.googleapis.com/auth/cloud-platform"]
}
deletion_protection = false
}
# 6. Dedicated Cloud TPU v5e Node Pool (ct5lp-hightpu-4t / 2x2 Topology)
resource "google_container_node_pool" "tpu_pool" {
provider = google-beta
name = "tpu-v5e-pool"
location = var.region
node_locations = [var.zone]
cluster = google_container_cluster.primary.name
node_count = 1
node_config {
machine_type = "ct5lp-hightpu-4t"
oauth_scopes = ["https://www.googleapis.com/auth/cloud-platform"]
labels = {
"cloud.google.com/gke-tpu-accelerator" = "tpu-v5-lite-podslice"
"cloud.google.com/gke-tpu-topology" = "2x2"
}
taint {
key = "google.com/tpu"
value = "present"
effect = "NO_SCHEDULE"
}
}
}
EOF
- מוודאים שנמצאים בספרייה gke-cnp-lab ומריצים את הפקודות הבאות
terraform initכדי להפעיל את ספריית העבודה. זה השלב הראשון, שבו מורידים את הספקים שנדרשים להגדרות האישיות שצוינו.הפקודהterraform plan -outיוצרת תוכנית ביצוע שמראה אילו פעולות Terraform תבצע כדי לפרוס את התשתית. הפקודה-outמאפשרת לשמור את תוכנית הביצוע בקובץ בינארי עם שם. אתם יכולים לראות מה יקרה בלי לבצע שינויים.terraform applyמריץ את העדכונים.
terraform init
terraform plan -out cnpgke
- מפעילים את תוכנית הביצוע. מכיוון שאתם מחילים את תוכנית ההרצה השמורה, היא תפעל באופן מיידי בלי לבקש אישור (התהליך עשוי להימשך בין 6 ל-10 דקות).
terraform apply cnpgke
- אימות פריסת התשתית:
gcloud container clusters get-credentials ${CLUSTER_NAME} --region ${REGION} --project=${PROJECT_ID}
echo -e "\n=== Verifying Cluster Nodes ==="
kubectl get nodes -L cloud.google.com/gke-tpu-accelerator,cloud.google.com/gke-tpu-topology

4. הגדרת רישום ביומן הרשת
לפני שפורסים עומסי עבודה וכללי מדיניות, מגדירים את GKE Network Logging כדי לתעד את הטלמטריה של החיבורים ולבצע ביקורת על החלטות לגבי מדיניות ב-Cloud Logging.
- בספרייה
gke-cnp-lab, יוצרים את מניפסט ההגדרות של NetworkLogging
cat << 'EOF' > 00-network-logging.yaml
apiVersion: networking.gke.io/v1alpha1
kind: NetworkLogging
metadata:
name: default
spec:
cluster:
allow:
delegate: false
log: true
deny:
delegate: false
log: true
EOF
- החלת ההגדרות על האשכול
kubectl apply -f 00-network-logging.yaml
kubectl get networklogging default
הערה: יכול להיות שתופיע אזהרה שהמשאב חסר ב-kubectl.kubernetes.io/last-applied-configuration annotation.. זה תקין וצפוי. GKE יוצר מראש אובייקט NetworkLogging כברירת מחדל כשהאשכול מופעל, והפקודה kubectl apply פשוט משתלטת על הניהול שלו כדי להחיל את הכללים החדשים.
5. פריסת בדיקות של עומס העבודה והלקוח ב-Gemma 4
תיצרו מרחבי שמות מבודדים כדי לבדוק הערכות מדיניות בכמה רמות:
-
gemma-llm: מכיל את עומס העבודה העיקרי של הסקת המסקנות של Gemma 4 (tier: ai-core). -
client-ns: מכיל את אפליקציית הלקוח המורשית לצ'אט RAG (tier: app-frontend). -
untrusted-tenant: מכיל קונטיינר בדיקה לא מורשה (tier: untrusted).
- ב-
gke-cnp-lab directory,, יוצרים את המניפסט01-namespaces-clients.yamlשמכיל את מרחבי השמות ואת ה-pods של לקוח הבדיקה:
cat << 'EOF' > 01-namespaces-clients.yaml
apiVersion: v1
kind: Namespace
metadata:
name: gemma-llm
labels:
tier: ai-core
---
apiVersion: v1
kind: Namespace
metadata:
name: client-ns
labels:
tier: app-frontend
---
apiVersion: v1
kind: Namespace
metadata:
name: untrusted-tenant
labels:
tier: untrusted
---
# Authorized Client Pod
apiVersion: apps/v1
kind: Deployment
metadata:
name: rag-agent-client
namespace: client-ns
labels:
app: rag-agent
spec:
replicas: 1
selector:
matchLabels:
app: rag-agent
template:
metadata:
labels:
app: rag-agent
spec:
containers:
- name: client
image: curlimages/curl:latest
command: ["/bin/sh", "-c", "sleep 86400"]
---
# Rogue / Untrusted Probe Pod
apiVersion: apps/v1
kind: Deployment
metadata:
name: rogue-client
namespace: untrusted-tenant
labels:
app: rogue-app
spec:
replicas: 1
selector:
matchLabels:
app: rogue-app
template:
metadata:
labels:
app: rogue-app
spec:
containers:
- name: probe
image: curlimages/curl:latest
command: ["/bin/sh", "-c", "sleep 86400"]
EOF
- מחילים את מרחבי השמות ואת פריסות הלקוח:
kubectl apply -f 01-namespaces-clients.yaml
kubectl get namespaces gemma-llm client-ns untrusted-tenant --show-labels
פריסת עומס עבודה (workload) של vLLM ו-Gemma
יוצרים את הסוד של Hugging Face. מחליפים את YOUR_ACTUAL_HUGGING_FACE_TOKEN באסימון:
export HF_TOKEN="YOUR_ACTUAL_HUGGING_FACE_TOKEN"
echo $HF_TOKEN
kubectl create secret generic hf-secret -n gemma-llm --from-literal=hf_token=${HF_TOKEN}
- יוצרים את המניפסט של עומס העבודה להסקת מסקנות של Gemma 4 שמטרגט את Cloud TPU v5e באמצעות vllm-tpu:
cat << 'EOF' > 02-gemma4-tpu-vllm.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: gemma-serving
namespace: gemma-llm
labels:
app: gemma-service
spec:
replicas: 1
selector:
matchLabels:
app: gemma-service
template:
metadata:
labels:
app: gemma-service
ai.gke.io/model: gemma4
ai.gke.io/inference-server: vllm-tpu
spec:
tolerations:
- key: "google.com/tpu"
operator: "Exists"
effect: "NoSchedule"
nodeSelector:
cloud.google.com/gke-tpu-accelerator: tpu-v5-lite-podslice
cloud.google.com/gke-tpu-topology: 2x2
containers:
- name: vllm-inference
image: vllm/vllm-tpu:latest
command: ["python3", "-m", "vllm.entrypoints.openai.api_server"]
args:
- "--model=google/gemma-4-E2B-it"
- "--tensor-parallel-size=4"
- "--host=0.0.0.0"
- "--port=8000"
- "--max-model-len=4096"
- "--max-num-batched-tokens=4096"
env:
- name: HF_TOKEN
valueFrom:
secretKeyRef:
name: hf-secret
key: hf_token
- name: HUGGING_FACE_HUB_TOKEN
valueFrom:
secretKeyRef:
name: hf-secret
key: hf_token
volumeMounts:
- mountPath: /dev/shm
name: dshm
ports:
- name: http-inference
containerPort: 8000
resources:
requests:
google.com/tpu: "4"
memory: "32Gi"
cpu: "8"
limits:
google.com/tpu: "4"
memory: "32Gi"
cpu: "8"
startupProbe:
httpGet:
path: /health
port: 8000
failureThreshold: 120
periodSeconds: 10
readinessProbe:
httpGet:
path: /health
port: 8000
periodSeconds: 5
volumes:
- name: dshm
emptyDir:
medium: Memory
---
apiVersion: v1
kind: Service
metadata:
name: gemma-service
namespace: gemma-llm
spec:
type: ClusterIP
selector:
app: gemma-service
ports:
- name: http
protocol: TCP
port: 8000
targetPort: 8000
EOF
- יוצרים את הפריסה.
kubectl apply -f 02-gemma4-tpu-vllm.yaml
- כדי לעקוב אחרי סטטוס ההשלמה, מריצים את הסקריפט הבא. ה-pods ימתינו עד להקצאת הצומת לפני שיוכלו להמשיך, וזה עשוי להימשך יותר מ-13 דקות.
echo " __|__"
echo " --@--(_|_)--@--"
echo ""
echo "Waiting for Gemma 4 TPU Pod to initialize and become Ready..."
# 1. Wait for the pod to be scheduled and reach the Running state
kubectl wait -n gemma-llm --for=condition=Ready pod -l app=gemma-service --timeout=600s
echo ""
echo "✅ Pod is Running! Streaming vLLM engine initialization logs..."
# 2. Stream logs until Uvicorn/vLLM is ready to serve
kubectl logs -n gemma-llm -l app=gemma-service -c vllm-inference -f | sed -u '/Application startup complete/q'
echo ""
echo "🚀 Gemma 4 on TPU v5e is ready for inference!"
6. הסבר על הערכת המדיניות ועל מצב ברירת המחדל
לפני שמחילים מדיניות רשת ב-Kubernetes, חשוב להבין איך ClusterNetworkPolicy של GKE ו-NetworkPolicy הרגיל של Kubernetes פועלים יחד ברמות הערכה שונות.
התנועה ב-GKE נבדקת באמצעות מודל קדימויות בכמה רמות:
Admin Tier ⟶ NetworkPolicy Tier ⟶ Baseline Tier ⟶ Default GKE Behavior

תהליך הבדיקה שלנו
===================================================
4-STAGE POLICY EVALUATION FLOW
===================================================
[Stage 1: Default State]
├── Pre-Policy State: Default Kubernetes allow-all
├── Ingress: client-ns ──▶ Gemma 4: ALLOWED (200 OK)
├── Ingress: untrusted ──▶ Gemma 4: ALLOWED (200 OK)
├── Egress: All Pods ──▶ Metadata: ALLOWED (Exposed)
└── Security Posture: ⚠️ Overly permissive
│
▼
[Stage 2: Baseline Lockdown]
├── Policy: ClusterNetworkPolicy (tier: Baseline)
├── Rule: Default-deny all cross-namespace ingress
├── Ingress: client-ns ──▶ Gemma 4: DROPPED (Timeout)
├── Ingress: untrusted ──▶ Gemma 4: DROPPED (Timeout)
└── Security Posture: 🛑 Zero-trust baseline active
│
▼
[Stage 3: Developer Delegation]
├── Policy: NetworkPolicy (tier: NetworkPolicy)
├── Rule: App team allows ingress from client-ns
├── Ingress: client-ns ──▶ Gemma 4: ALLOWED (200 OK)
├── Ingress: untrusted ──▶ Gemma 4: DROPPED (Timeout)
└── Security Posture: 🔀 App isolation achieved
│
▼
[Stage 4: Admin Guardrail]
├── Policy: ClusterNetworkPolicy (tier: Admin)
├── Rule: Cluster-wide metadata drop (169.254.169.254)
├── Egress: All Pods ──▶ Metadata: DROPPED (Timeout)
├── Dev NetPol bypass attempt: OVERRIDDEN (Blocked)
└── Security Posture: 🔒 Enforced guardrail
===================================================
הגדרת בסיס (בדיקה לפני המדיניות)
מוודאים שהמודל Gemma 4 תקין ומשרת בקשות להסקת מסקנות. ב-Kubernetes רגיל בלי כללי מדיניות, כל תעבורת הנתונים בין מרחבי שמות מותרת באופן משתמע.
- בודקים את ההסקה מהלקוח המורשה ב-
client-ns:
kubectl exec -n client-ns deploy/rag-agent-client -- curl --connect-timeout 3 -s -X POST \
http://gemma-service.gemma-llm.svc.cluster.local:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "google/gemma-4-E2B-it",
"messages": [{"role": "user", "content": "Explain zero trust in one short sentence."}],
"max_tokens": 40
}' | jq -r '.choices[0].message.content'
- בדיקת ההסקה מהלקוח הלא מורשה ב-
untrusted-tenant:
kubectl exec -n untrusted-tenant deploy/rogue-client -- curl --connect-timeout 3 -s -X POST \
http://gemma-service.gemma-llm.svc.cluster.local:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "google/gemma-4-E2B-it",
"messages": [{"role": "user", "content": "Explain zero trust in one short sentence."}],
"max_tokens": 40
}' | jq -r '.choices[0].message.content'
- בודקים אם Pods בכל שלושת מרחבי השמות יכולים להגיע לשרת המטא-נתונים (169.254.169.254):
echo -e "\n[1/3] Testing Metadata access from Gemma serving pod (gemma-llm)..."
kubectl exec -n gemma-llm deploy/gemma-serving -- curl --connect-timeout 3 -s -w "\nHTTP Code: %{http_code}\n" -H "Metadata-Flavor: Google" \
http://169.254.169.254/computeMetadata/v1/instance/zone || echo "🛑 RESULT: Connection Timed Out (Blocked)"
echo -e "\n[2/3] Testing Metadata access from authorized client (client-ns)..."
kubectl exec -n client-ns deploy/rag-agent-client -- curl --connect-timeout 3 -s -w "\nHTTP Code: %{http_code}\n" -H "Metadata-Flavor: Google" \
http://169.254.169.254/computeMetadata/v1/instance/zone || echo "🛑 RESULT: Connection Timed Out (Blocked)"
echo -e "\n[3/3] Testing Metadata access from rogue client (untrusted-tenant)..."
kubectl exec -n untrusted-tenant deploy/rogue-client -- curl --connect-timeout 3 -s -w "\nHTTP Code: %{http_code}\n" -H "Metadata-Flavor: Google" \
http://169.254.169.254/computeMetadata/v1/instance/zone || echo "🛑 RESULT: Connection Timed Out (Blocked)"
7. בדיקת הערכה של Baseline ו-Network Policy Tier
בשלב הזה, בתור מנהלי האשכול, תפעילו שכבת בסיס של ClusterNetworkPolicy כדי להגדיר גישת דחייה כברירת מחדל לכל האשכול. כל תקשורת בין מרחבי שמות שלא אושרה במפורש על ידי שכבה גבוהה יותר תבוטל.
הגדרת גישה למודל האבטחה של אפס אמון (רמת בסיס)
- יוצרים את מניפסט המדיניות של
03-baseline-deny.yamlBaseline-tier:
cat << 'EOF' > 03-baseline-deny.yaml
apiVersion: policy.networking.k8s.io/v1alpha2
kind: ClusterNetworkPolicy
metadata:
name: baseline-default-deny
spec:
tier: Baseline
priority: 1000
subject:
namespaces: {}
ingress:
# 1. Allow CoreDNS Ingress cluster-wide so DNS resolution never breaks
- name: allow-dns-ingress
action: Accept
from:
- namespaces: {}
protocols:
- udp:
destinationPort:
number: 53
- tcp:
destinationPort:
number: 53
# 2. Default Deny all other cross-namespace ingress
- name: baseline-deny-cross-ns
action: Deny
from:
- namespaces: {}
EOF
- מחילים את המדיניות של רמת הבסיס:
kubectl apply -f 03-baseline-deny.yaml
- בדיקת הקישוריות של הלקוח מ-
client-ns.
kubectl exec -n client-ns deploy/rag-agent-client -- curl --connect-timeout 3 -s -X POST \
http://gemma-service.gemma-llm.svc.cluster.local:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "google/gemma-4-E2B-it",
"messages": [{"role": "user", "content": "Explain zero trust in one short sentence."}],
"max_tokens": 40
}' | jq -r '.choices[0].message.content'
- בודקים את ההסקה מהלקוח הלא מורשה והזדוני בדייר הלא מהימן:
kubectl exec -n untrusted-tenant deploy/rogue-client -- curl --connect-timeout 3 -s -X POST \
http://gemma-service.gemma-llm.svc.cluster.local:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "google/gemma-4-E2B-it",
"messages": [{"role": "user", "content": "Explain zero trust in one short sentence."}],
"max_tokens": 40
}' | jq -r '.choices[0].message.content'
הענקת גישה למפתחים (רמת NetworkPolicy)
עכשיו צוות האפליקציה gemma-llm יחיל NetworkPolicy רגיל ברמת מרחב השמות כדי לאשר תנועה מורשית מ-client-ns.
- יוצרים את המניפסט NetworkPolicy של המפתח
04-app-networkpolicy.yaml:
cat << 'EOF' > 04-app-networkpolicy.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-rag-client
namespace: gemma-llm
spec:
podSelector:
matchLabels:
app: gemma-service
policyTypes:
- Ingress
ingress:
# Explicitly permit ingress only from pods matching app: rag-agent in client-ns
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: client-ns
podSelector:
matchLabels:
app: rag-agent
ports:
- protocol: TCP
port: 8000
EOF
- מחילים את המדיניות:
kubectl apply -f 04-app-networkpolicy.yaml
- בדיקת הקישוריות של הלקוח מ-
client-ns
kubectl exec -n client-ns deploy/rag-agent-client -- curl --connect-timeout 3 -s -X POST \
http://gemma-service.gemma-llm.svc.cluster.local:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "google/gemma-4-E2B-it",
"messages": [{"role": "user", "content": "Explain zero trust in one short sentence."}],
"max_tokens": 40
}' | jq -r '.choices[0].message.content'
- בודקים את הקישוריות מהלקוח הזדוני ב-
untrusted-tenant:
kubectl exec -n untrusted-tenant deploy/rogue-client -- curl --connect-timeout 3 -s -X POST \
http://gemma-service.gemma-llm.svc.cluster.local:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "google/gemma-4-E2B-it",
"messages": [{"role": "user", "content": "Explain zero trust in one short sentence."}],
"max_tokens": 40
}' | jq -r '.choices[0].message.content'
8. הערכה של אכיפת שכבות הגנה גלובליות (רמת אדמין)
בתור מנהלי האשכול,תאכפו עכשיו מדיניות ברמת האדמין (ClusterNetworkPolicy) בראש סדר ההערכה של GKE. הרמה הזו נבדקת ראשונה, וכל כלל תואם מקבל עדיפות מיידית על פני כל הרמות האחרות. אם אין התאמה, החבילה מועברת להערכה על ידי השכבות הנמוכות יותר (NetworkPolicy ו-Baseline).
בשלב הזה, תפעילו מדיניות ש:
- Cluster-wide Egress Guardrail: חוסם את כל הפודים בכל מרחב שמות מלהגיע לשרת המטא-נתונים של Google Cloud (
169.254.169.254/32). - תמיכה ב-DNS: מאפשרת את כל תעבורת הנתונים של DNS ביציאה 53, כדי ש-Pods יוכלו לפתור שמות מארחים (תוך התחשבות במטמוני ה-DNS המקומיים של GKE).
- העברת סמכויות לתעבורת נתונים נכנסת ויוצאת: העברת כל שאר ההחלטות לגבי תעבורת הנתונים באמצעות הפעולה Pass, כדי שכללי המדיניות הרגילים של המפתחים לגבי הרשת וכללי ברירת המחדל הבסיסיים לדחייה יוכלו לקבל את ההחלטות הסופיות.
- יוצרים את המניפסט של המדיניות ברמת האדמין של
05-admin-guardrails.yamlשמטרגט את כל מרחבי השמות
cat << 'EOF' > 05-admin-guardrails.yaml
apiVersion: policy.networking.k8s.io/v1alpha2
kind: ClusterNetworkPolicy
metadata:
name: admin-global-guardrails
spec:
tier: Admin
priority: 10
subject:
namespaces: {}
ingress:
- name: delegate-all-ingress
action: Pass
from:
- namespaces: {}
egress:
- name: allow-all-dns
action: Accept
to:
- networks:
- 0.0.0.0/0
protocols:
- udp:
destinationPort:
number: 53
- tcp:
destinationPort:
number: 53
- name: block-metadata-server-global
action: Deny
to:
- networks:
- 169.254.169.254/32
- name: delegate-other-egress
action: Pass
to:
- namespaces: {}
EOF
- מחילים את המדיניות של רמת האדמין:
kubectl apply -f 05-admin-guardrails.yaml
- בודקים את זליגת המטא-נתונים בכל שלושת מרחבי השמות:
echo -e "\n[1/3] Testing Metadata access from Gemma serving pod (gemma-llm)..."
kubectl exec -n gemma-llm deploy/gemma-serving -- curl --connect-timeout 3 -s -w "\nHTTP Code: %{http_code}\n" -H "Metadata-Flavor: Google" \
http://169.254.169.254/computeMetadata/v1/instance/zone || echo "🛑 RESULT: Connection Timed Out (Blocked)"
echo -e "\n[2/3] Testing Metadata access from authorized client (client-ns)..."
kubectl exec -n client-ns deploy/rag-agent-client -- curl --connect-timeout 3 -s -w "\nHTTP Code: %{http_code}\n" -H "Metadata-Flavor: Google" \
http://169.254.169.254/computeMetadata/v1/instance/zone || echo "🛑 RESULT: Connection Timed Out (Blocked)"
echo -e "\n[3/3] Testing Metadata access from rogue client (untrusted-tenant)..."
kubectl exec -n untrusted-tenant deploy/rogue-client -- curl --connect-timeout 3 -s -w "\nHTTP Code: %{http_code}\n" -H "Metadata-Flavor: Google" \
http://169.254.169.254/computeMetadata/v1/instance/zone || echo "🛑 RESULT: Connection Timed Out (Blocked)"
- בדיקת הקישוריות של הלקוח מ-
client-ns
kubectl exec -n client-ns deploy/rag-agent-client -- curl --connect-timeout 3 -s -X POST \
http://gemma-service.gemma-llm.svc.cluster.local:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "google/gemma-4-E2B-it",
"messages": [{"role": "user", "content": "Explain zero trust in one short sentence."}],
"max_tokens": 40
}' | jq -r '.choices[0].message.content'
- כדי לדמות ניסיון של מפתח לעקוף את המדיניות, מפעילים מדיניות NetworkPolicy שמאפשרת את כל תעבורת הנתונים היוצאת בתוך gemma-llm:
cat << 'EOF' > 06-bypass-attempt.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: dev-allow-all-egress
namespace: gemma-llm
spec:
podSelector: {}
policyTypes:
- Egress
egress:
- {}
EOF
kubectl apply -f 06-bypass-attempt.yaml
- בודקים אם המעקף של המפתח יכול לבטל את החסימה של הגישה למטא-נתונים:
kubectl exec -n gemma-llm deploy/gemma-serving -- curl --connect-timeout 3 -s -w "\nHTTP Code: %{http_code}\n" -H "Metadata-Flavor: Google" \
http://169.254.169.254/computeMetadata/v1/instance/zone || echo "🛑 RESULT: Connection Timed Out (Admin Guardrail Overrode Developer Policy)"
9. אימות אינטראקטיבי באמצעות Hubble ו-Cloud Logging
כדי לצפות באכיפת המדיניות בזמן אמת ובהחלטות לגבי חבילות ביקורת באשכול, תשתמשו בסשן של Cloud Shell עם שתי חלוניות שמשלב מעקב אחר חבילות eBPF ברמת הליבה עם ביצוע בדיקה אוטומטית.
- לוחצים על הסמל "+" בתפריט של Cloud Shell כדי לפתוח כרטיסיית טרמינל שנייה.
- בכרטיסייה החדשה, מריצים את הפקודות הבאות כדי ליצור את הכינוי ולהתחיל לצפות בתנועה בזמן אמת במרחב השמות
gemma-llm:
# Create an alias to execute Hubble natively inside the relay pod
alias hubble="kubectl exec -it -n gke-managed-dpv2-observability deploy/hubble-relay -c hubble-cli -- hubble"
# Observe real-time traffic to and from the Gemma namespace
hubble observe --follow --namespace gemma-llm
- חוזרים לכרטיסייה הראשונה של Cloud Shell ומוודאים שנמצאים בספרייה
gke-cnp-lab.
cd ~/gke-cnp-lab
- מריצים את הבלוק הבא כדי ליצור ולהריץ את סקריפט האימות האוטומטי. הסקריפט הזה מריץ את בדיקות התנועה הנדרשות ושולח שאילתות ל-Cloud Logging באמצעות סכימת המטא-נתונים המדויקת ברמת הבסיס של GKE, כדי להציג את הכללים המדויקים של Dataplane V2 שהעריכו את הבקשות:
cat << 'EOF' > verify-suite.sh
#!/usr/bin/env bash
set -e
echo "========================================================"
echo " 🧪 RUNNING MULTI-TIER POLICY VERIFICATION SUITE"
echo "========================================================"
# Test 1: Ingress Authorized Client -> Gemma (Allowed by NetworkPolicy)
echo -e "\n[1/5] Testing Authorized Client -> Gemma Serving..."
if kubectl exec -n client-ns deploy/rag-agent-client -- \
curl -s --connect-timeout 3 \
http://gemma-service.gemma-llm.svc.cluster.local:8000/v1/chat/completions > /dev/null; then
echo " ✅ RESULT: FORWARDED (Allowed by Developer Network Policy)"
else
echo " 🛑 RESULT: FAILED"
fi
# Test 2: Ingress Rogue Client -> Gemma (Blocked by Baseline Tier)
echo -e "\n[2/5] Testing Unauthorized Rogue Tenant -> Gemma Serving..."
if kubectl exec -n untrusted-tenant deploy/rogue-client -- \
curl -s --connect-timeout 3 \
http://gemma-service.gemma-llm.svc.cluster.local:8000/v1/chat/completions > /dev/null; then
echo " ⚠️ RESULT: UNEXPECTED ALLOW"
else
echo " 🛑 RESULT: DROPPED (Blocked by Baseline Tier)"
fi
# Test 3: Egress Gemma -> GCP Metadata Server (Blocked by Admin Tier)
echo -e "\n[3/5] Testing Metadata access from Gemma serving pod (gemma-llm)..."
kubectl exec -n gemma-llm deploy/gemma-serving -- \
curl --connect-timeout 3 -s -w "\nHTTP Code: %{http_code}\n" -H "Metadata-Flavor: Google" \
http://169.254.169.254/computeMetadata/v1/instance/zone || echo " 🛑 RESULT: Connection Timed Out (Blocked)"
# Test 4: Egress Authorized Client -> GCP Metadata Server (Blocked by Admin Tier)
echo -e "\n[4/5] Testing Metadata access from authorized client (client-ns)..."
kubectl exec -n client-ns deploy/rag-agent-client -- \
curl --connect-timeout 3 -s -w "\nHTTP Code: %{http_code}\n" -H "Metadata-Flavor: Google" \
http://169.254.169.254/computeMetadata/v1/instance/zone || echo " 🛑 RESULT: Connection Timed Out (Blocked)"
# Test 5: Egress Rogue Client -> GCP Metadata Server (Blocked by Admin Tier)
echo -e "\n[5/5] Testing Metadata access from rogue client (untrusted-tenant)..."
kubectl exec -n untrusted-tenant deploy/rogue-client -- \
curl --connect-timeout 3 -s -w "\nHTTP Code: %{http_code}\n" -H "Metadata-Flavor: Google" \
http://169.254.169.254/computeMetadata/v1/instance/zone || echo " 🛑 RESULT: Connection Timed Out (Blocked)"
echo -e "\n========================================================"
echo " 📋 FETCHING AUDIT TRAIL FROM CLOUD LOGGING"
echo "========================================================"
echo "Waiting 12 seconds for telemetry ingestion..."
sleep 12
echo -e "\n🔍 1. Validating Baseline Tier (Rogue Client Block):"
gcloud logging read '
resource.type="k8s_node"
logName=~"logs/policy-action"
jsonPayload.disposition="deny"
jsonPayload.src.namespace="untrusted-tenant"
jsonPayload.dest.namespace="gemma-llm"
' --limit=1 --format="table(
timestamp.date('%H:%M:%S', tz=LOCAL):label=TIME,
jsonPayload.src.namespace:label=SRC_NS,
jsonPayload.dest.namespace:label=DEST_NS,
jsonPayload.policies[0].name:label=BLOCKED_BY_POLICY
)"
echo -e "\n🔍 2. Validating Admin Tier (Metadata Block across all namespaces):"
gcloud logging read '
resource.type="k8s_node"
logName=~"logs/policy-action"
jsonPayload.disposition="deny"
jsonPayload.connection.dest_ip="169.254.169.254"
' --limit=3 --format="table(
timestamp.date('%H:%M:%S', tz=LOCAL):label=TIME,
jsonPayload.src.namespace:label=SRC_NS,
jsonPayload.connection.dest_ip:label=DEST_IP,
jsonPayload.policies[0].name:label=BLOCKED_BY_POLICY
)"
EOF
chmod +x verify-suite.sh
./verify-suite.sh
- בזמן שהסקריפט פועל בכרטיסייה הראשונה, צופים בפלט של Hubble בכרטיסייה השנייה.
ב-Hubble (כרטיסייה 2): בקשות ה-HTTP המורשות יועברו ויוצגו בירוק. במקרה של תנועה חסומה, יוצגו אירועים אדומים (DROPPED). הדייר הנוכל: החיבור מועבר על ידי שכבת הניהול ונחסם בשכבת הבסיס. שאילתות לקבלת מטא נתונים: הבקשה שלה לגשת לפרטי הכניסה לענן נחסמת מיד ברמת הניהול.

ב-Cloud Logging (כרטיסייה 1): הסקריפט מסיים את הפעולה על ידי שליפת רשומות הביקורת המדויקות שנשלחו אל Google Cloud Logging. הדוח מוכיח באופן מפורש איזו רמה (אדמין או בסיסית) ואיזו מדיניות היו אחראיות לעצירת התנועה הלא מורשית, בפורמט עם חותמות זמן מקומיות שמותאמות לאזור הזמן.

10. הסרת המשאבים
- סגירת הכרטיסייה השנייה
- חוזרים לכרטיסייה הראשונה של Cloud Shell.
- מוחקים את המניפסטים, מרחבי השמות והסודות שנפרסו ב-Kubernetes לפי הסדר הבא:
cd ~/gke-cnp-lab
# Delete policy and verification scripts
rm -f verify-suite.sh
kubectl delete -f 05-admin-guardrails.yaml --ignore-not-found
kubectl delete -f 04-app-networkpolicy.yaml --ignore-not-found
kubectl delete -f 03-baseline-deny.yaml --ignore-not-found
kubectl delete -f 00-network-logging.yaml --ignore-not-found
# Delete workload deployments, secrets, and namespaces
kubectl delete -f 02-gemma4-tpu-vllm.yaml --ignore-not-found
kubectl delete secret hf-secret -n gemma-llm --ignore-not-found
kubectl delete -f 01-namespaces-clients.yaml --ignore-not-found
- מנקים את התשתית באמצעות הפקודה הבאה, ומקלידים
yesכדי לאשר:
terraform destroy --auto-approve
- מוודאים שכל המשאבים בפרויקט הפעיל נסגרו כמו שצריך:
gcloud container clusters list --region=us-west4
gcloud compute networks list
- מוחקים את הספרייה
gke-cnp-labואת התוכן שלה:
cd
rm -r gke-cnp-lab
11. מזל טוב
סיימתם את ה-Codelab בנושא אבטחת עומסי עבודה של הסקת AI באמצעות ClusterNetworkPolicy ב-GKE.
בשיעור ה-Lab המעשי הזה תכננתם ואימתם ארכיטקטורת רשת חזקה, רב-שכבתית ומעמיקה להגנה על עומסי עבודה רגישים ופרטיים של LLM ב-Google Kubernetes Engine.
מה השגתם:
- תשתית כקוד: הקצאה דינמית של אשכול GKE מסוג Standard פרטי ומאובטח ברמה גבוהה, רשת משנה אזורית פרטית וחומרת Cloud TPU v5e ייעודית באמצעות Terraform.
- טלמטריה וניראות: הגדרת רישום ביומן של רשת Dataplane V2 לצד מעקב eBPF Hubble ברמת הליבה (kernel) כדי להשיג ניראות עמוקה של חבילות בזמן ריצה.
- מצב אבטחה היררכי: למשל, הפרדה ברורה של חובות תפעוליות בין שלוש רמות הערכה נפרדות:
- רמת אדמין: אמצעי הגנה על תעבורת נתונים יוצאת (egress) שנאכפים בכל האשכול, שאי אפשר לעקוף אותם ושחוסמים גישה רוחבית לפרטי הכניסה של המטא-נתונים (169.254.169.254).
- שכבת NetworkPolicy: גבולות כניסה מוקצים ברמת מרחב השמות למפתחי אפליקציות, כדי לאשר חיבורים של לקוחות בסביבת הייצור באופן בטוח.
- רמת בסיס: בניית רשת ביטחון חלופית לכל האשכול, שדוחה כברירת מחדל תנועה לא מורשית של דיירים.
- ביקורת בזמן אמת: הפעלה של חבילת מקרים לבדיקה מלאה, צפייה בזמן אמת בחבילות שנמחקו ברמת הליבה של Linux, ושאילתות על ביקורות מובְנות של פעולות מדיניות ישירות ב-Google Cloud Logging