1. Übersicht
In diesem Lab lernen Sie ClusterNetworkPolicy in der Google Kubernetes Engine (GKE) kennen. ClusterNetworkPolicy wird für verschiedene Anwendungsfälle angewendet und kann clusterweit angewendet und auf der Administrator- und Baseline-Ebene erzwungen werden. In diesem Beispiel wird die Steuerung mit einer LLM-Bereitstellung (Gemma) in GKE mit TPUs, vLLM und Validierung demonstriert.
Sie arbeiten mit den folgenden Technologien:
- Google Kubernetes Engine (GKE): Die grundlegende Containerorchestrierungsplattform zum Hosten privater, skalierbarer Arbeitslasten.
- ClusterNetworkPolicy (CNP): Clusterbezogene mehrstufige Netzwerkrichtlinien, die SecOps-Leitlinien auf den Ebenen „Admin“, „NetworkPolicy“ und „Baseline“ ermöglichen.
- vLLM TPU Inference Engine: Hochdurchsatz-Inferenzbereitstellung des schlanken Gemma 4-Modells (google/gemma-4-E2B-it) auf Google TPUs mit OpenAI-kompatiblen REST-Endpunkten.
Rollen und Personas
Die Netzwerkrichtlinien-API ist auf bestimmte Organisationsrollen ausgerichtet:
- Clusteradministrator (
ClusterNetworkPolicy): Erzwingt Sicherheitsmaßnahmen für die Cluster auf Administrator- und Baseline-Ebene. - Namespace-Administrator (
NetworkPolicy): Verwaltet Grenzen und Isolation auf Mandantenebene innerhalb bestimmter Namespaces. - Anwendungsentwickler (
NetworkPolicy): Definiert detaillierte Regeln für anwendungsspezifische Mikrodienste.
Zum Konfigurieren dieser Umgebung stellen Sie eine benutzerdefinierte private VPC, Cloud NAT und einen privaten GKE-Cluster mit einem dedizierten TPU-Knotenpool bereit.
Um Ihren Cluster zu sichern, erstellen Sie mehrstufige ClusterNetworkPolicy-Ressourcen zusammen mit standardmäßigen NetworkPolicy-Objekten, um strenge Netzwerkrichtlinien durchzusetzen. Anschließend stellen Sie eine KI-Arbeitslast bereit, die vLLM und Google Cloud-TPUs verwendet, um das Gemma 4-Modell bereitzustellen.
Schließlich erstellen Sie eine funktionierende Baseline, wenden die Sicherheitsrichtlinien an und konfigurieren NetworkLogging mit Cloud Logging und Live-Hubble-eBPF-Beobachtbarkeitsstreams, um diese Richtlinienentscheidungen zu prüfen und Paketverluste zu verifizieren. Alle Konfigurationen verwenden eine Kombination aus Terraform, gcloud und kubectl..
Aufgaben in diesem Lab:
- Stellen Sie ein privates Netzwerk, einen GKE-Cluster und Cloud TPU v5e-Beschleuniger mit Terraform bereit.
- GKE NetworkLogging konfigurieren, um Verbindungstelemetrie aufzuzeichnen
- Stellen Sie Gemma 4 E2B auf einer TPU mit dem offiziellen vllm/vllm-tpu-Container-Image bereit.
- Erstellen Sie eine funktionierende Referenz, die die End-to-End-Konnektivität belegt, bevor Sie Einschränkungen anwenden.
- Wenden Sie eine
Baseline-tier-ClusterNetworkPolicy an, um nicht autorisierten Traffic zwischen Namespaces standardmäßig abzulehnen. - Wenden Sie eine vom Entwickler verwaltete NetworkPolicy an, um den Zugriff an autorisierte Clients zu delegieren.
- Wenden Sie eine
Admin-tier-ClusterNetworkPolicy an, um globale Sicherheitsmechanismen für ausgehenden Traffic zu erzwingen, die Entwicklereinstellungen überschreiben. - Live-Paketverluste und -weiterleitungen mit Hubble-eBPF-Streams und Cloud Logging prüfen.
In diesem Lab erstellen Sie das folgende Muster.
Abbildung 1: 
2. Google Cloud-Dienste einrichten
Umgebung zum selbstbestimmten Lernen einrichten
- Melden Sie sich in der Google Cloud Console an und erstellen Sie ein neues Projekt oder verwenden Sie ein vorhandenes Projekt. Wenn Sie noch kein Gmail- oder Google Workspace-Konto haben, müssen Sie eines erstellen.



- Der Projektname ist der Anzeigename für die Teilnehmer dieses Projekts. Es handelt sich um einen String, der nicht von Google APIs verwendet wird. Sie können sie jederzeit aktualisieren.
- Die Projekt-ID ist für alle Google Cloud-Projekte eindeutig und unveränderlich (kann nach dem Festlegen nicht mehr geändert werden). Die Cloud Console generiert automatisch einen eindeutigen String. In der Regel ist es egal, wie dieser lautet. In den meisten Code-Labs müssen Sie auf Ihre Projekt-ID verweisen (normalerweise als
PROJECT_IDgekennzeichnet). Wenn Ihnen die generierte ID nicht gefällt, können Sie eine andere zufällige ID generieren. Alternativ können Sie auch einen eigenen Namen ausprobieren und prüfen, ob er verfügbar ist. Sie kann nach diesem Schritt nicht mehr geändert werden und bleibt für die Dauer des Projekts bestehen. - Zur Information: Es gibt einen dritten Wert, eine Projektnummer, die von einigen APIs verwendet wird. Weitere Informationen zu allen drei Werten finden Sie in der Dokumentation.
- Als Nächstes müssen Sie in der Cloud Console die Abrechnung aktivieren, um Cloud-Ressourcen und ‑APIs verwenden zu können. Die Durchführung dieses Codelabs kostet, wenn überhaupt, nur wenig. Wenn Sie Ressourcen beenden möchten, um über diese Anleitung hinausgehende Kosten zu vermeiden, können Sie die erstellten Ressourcen oder das Projekt löschen. Neue Google Cloud-Nutzer kommen für das kostenlose Testprogramm mit einem Guthaben von 300 $ infrage.
Cloud Shell starten
Google Cloud kann zwar remote von Ihrem Laptop aus bedient werden, in diesem Codelab verwenden Sie jedoch die Google Cloud Shell, eine Befehlszeilenumgebung, die in der Cloud ausgeführt wird.
Klicken Sie in der Google Cloud Console rechts oben in der Symbolleiste auf das Cloud Shell-Symbol:

Die Bereitstellung und Verbindung mit der Umgebung sollte nur wenige Augenblicke dauern. Wenn der Vorgang abgeschlossen ist, wird in etwa Folgendes angezeigt:

Diese virtuelle Maschine verfügt über sämtliche Entwicklertools, die Sie benötigen. Sie bietet ein Basisverzeichnis mit 5 GB nichtflüchtigem Speicher und läuft in Google Cloud, was die Netzwerkleistung und Authentifizierung erheblich verbessert. Alle Aufgaben in diesem Codelab können in einem Browser erledigt werden. Sie müssen nichts installieren.
3. Umgebung mit Terraform einrichten
Für dieses Lab benötigen Sie Folgendes:
- Sie benötigen Zugriff auf das TPU v5e-Kontingent in der ausgewählten Region und Zone, z. B. (
us-central1-a, us-west4-a, us-south1-a, us-west1-c, europe-west4-b). Folgen Sie der TPU-Anleitung und aktivieren Sie das TPU-Kontingent, um Zugriff zu erhalten. - Dies ist eine kleine Bereitstellung, für die 4 TPU v5e-Chips (
ct5lp-hightpu-4t),) erforderlich sind, die ein 2x2-Slice in einer einzelnen Zone bilden.
Hugging Face verwenden
- Hugging Face-Token: Ein Zugriffstoken ist erforderlich, um die Gemma-Modellgewichtungen herunterzuladen.
- Akzeptieren Sie alle erforderlichen Nutzungsvereinbarungen.
Wir erstellen eine benutzerdefinierte VPC mit Firewallregeln, ein Subnetz und dann einen Autopilot-Cluster. Öffnen Sie die Cloud Console und wählen Sie das Projekt aus, das Sie verwenden möchten.
- Öffnen Sie die Cloud Shell oben rechts in der Konsole. Achten Sie darauf, dass die richtige Projekt-ID in der Cloud Shell angezeigt wird, und bestätigen Sie alle Aufforderungen, den Zugriff zu erlauben.

- Erstellen Sie ein Verzeichnis mit dem Namen
gke-cnp-lab,und wechseln Sie in dieses Verzeichnis.
mkdir -p gke-cnp-lab && cd gke-cnp-lab
- Erstellen Sie jetzt einige Variablen. Hinweis: Bearbeiten Sie REGION und ZONE, um den tatsächlichen Standort anzugeben, an dem Sie Zugriff auf die TPU-Ressourcen haben. Die im Lab verwendete Standardregion
us-west4und ‑zoneus-west4-a
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"
- Speichern Sie Ihre Konfigurationsvariablen in einem Hilfsskript im Verzeichnis gke-cnp-lab. Wenn die Verbindung zu Ihrer Cloud Shell-Sitzung getrennt oder die Sitzung zurückgesetzt wird, können Sie Ihre aktiven Variablen schnell wiederherstellen, indem Sie
source ./env.shausführen.
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
- Fügen Sie nun einige Konfigurationsdateien hinzu. Dadurch werden die Dateien terraform.tfvars, variables.tf und main.tf im Verzeichnis gke-cnp-lab erstellt.
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
- Prüfen Sie, ob Sie sich im Verzeichnis gke-cnp-lab befinden, und führen Sie die folgenden Befehle aus:
terraform initInitialisiert das Arbeitsverzeichnis. Dies ist der erste Schritt. Dabei werden die für die angegebene Konfiguration erforderlichen Anbieter heruntergeladen.terraform plan -outgeneriert einen Ausführungsplan, der zeigt, welche Aktionen Terraform zum Bereitstellen Ihrer Infrastruktur ausführt. Mit-outkönnen Sie den Ausführungsplan in einer benannten Binärdatei speichern. Sie können sich ansehen, was passieren würde, ohne Änderungen vorzunehmen.terraform applyführt die Aktualisierungen aus.
terraform init
terraform plan -out cnpgke
- Wenden Sie den Ausführungsplan an. Da Sie den gespeicherten Ausführungsplan anwenden, wird er sofort ausgeführt, ohne dass Sie zur Bestätigung aufgefordert werden. (Dies kann 6–10 Minuten dauern.)
terraform apply cnpgke
- Prüfen Sie die Infrastrukturbereitstellung:
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. Netzwerkprotokollierung konfigurieren
Bevor Sie Arbeitslasten und Richtlinien bereitstellen, konfigurieren Sie das GKE-Netzwerk-Logging, um Verbindungstelemetrie und Audit-Richtlinienentscheidungen in Cloud Logging aufzuzeichnen.
- Erstellen Sie im Verzeichnis
gke-cnp-labdas NetworkLogging-Konfigurationsmanifest.
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
- Konfiguration auf den Cluster anwenden
kubectl apply -f 00-network-logging.yaml
kubectl get networklogging default
Hinweis: Möglicherweise wird eine Warnung angezeigt, dass die Ressource kubectl.kubernetes.io/last-applied-configuration annotation. fehlt. Das ist völlig normal und zu erwarten. GKE erstellt beim Start des Clusters ein Standard-NetworkLogging-Objekt. Mit „kubectl apply“ wird die Verwaltung dieses Objekts übernommen, um Ihre neuen Regeln anzuwenden.
5. Gemma 4-Arbeitslast und Clientprüfungen bereitstellen
Sie erstellen isolierte Namespaces, um mehrstufige Richtlinienbewertungen zu testen:
gemma-llm: Enthält die Kernarbeitslast für die Gemma 4-Inferenz (tier: ai-core).client-ns: Enthält den autorisierten RAG-Chatclient (tier: app-frontend).untrusted-tenant: Enthält einen nicht autorisierten Prüfcontainer (tier: untrusted).
- Erstellen Sie im Verzeichnis
gke-cnp-lab directory,das Manifest01-namespaces-clients.yamlmit den Namespaces und Testclient-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
- Wenden Sie die Namespaces und Client-Deployments an:
kubectl apply -f 01-namespaces-clients.yaml
kubectl get namespaces gemma-llm client-ns untrusted-tenant --show-labels
Arbeitslast-vLLM und Gemma bereitstellen
Erstellen Sie Ihr Hugging Face-Secret. Ersetzen Sie YOUR_ACTUAL_HUGGING_FACE_TOKEN durch Ihr 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}
- Erstellen Sie das Manifest für die Gemma 4-Inferenzarbeitslast, das auf Cloud TPU v5e mit vllm-tpu abzielt:
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
- Erstellen Sie das Deployment.
kubectl apply -f 02-gemma4-tpu-vllm.yaml
- Führen Sie das folgende Script aus, um den Abschlussstatus zu überwachen. Die Pods warten, bis der Knoten bereitgestellt wurde, bevor sie fortfahren können. Dies kann 13 Minuten oder länger dauern.
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. Richtlinienbewertung und Standardstatus
Bevor Sie Netzwerkrichtlinien anwenden, sollten Sie sich damit vertraut machen, wie GKE-ClusterNetworkPolicy und die Standard-Kubernetes-NetworkPolicy auf verschiedenen Auswertungsebenen interagieren.
Der Traffic in GKE wird anhand eines mehrstufigen Prioritätsmodells bewertet:
Admin Tier ⟶ NetworkPolicy Tier ⟶ Baseline Tier ⟶ Default GKE Behavior

Unser Testablauf
===================================================
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
===================================================
Basislinie festlegen (Test vor der Richtlinie)
Prüfen Sie, ob das Gemma 4-Modell fehlerfrei ist und Inferenzanfragen verarbeitet. In Standard-Kubernetes ohne Richtlinien ist der gesamte Namespace-übergreifende Traffic implizit zulässig.
- Testen Sie die Inferenz vom autorisierten Client in
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'
- Testen Sie die Inferenz vom nicht autorisierten Rogue-Client in
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'
- Testen Sie, ob Pods in allen drei Namespaces den Metadatenserver (169.254.169.254) erreichen können:
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. Bewertungstests für Baseline und Netzwerkrichtlinien-Tier
In diesem Schritt wenden Sie als Clusteradministrator eine Baseline-StufeClusterNetworkPolicy an, um eine clusterweite Fallback-Standardeinstellung für die Ablehnung festzulegen. Jegliche namespaceübergreifende Kommunikation, die nicht explizit von einer höheren Ebene zugelassen wird, wird verworfen.
Zero-Trust-Status einrichten (Baseline-Stufe)
- Erstellen Sie das Richtlinienmanifest für die Baseline-Stufe
03-baseline-deny.yaml:
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
- Wenden Sie die Richtlinie für die Baseline-Stufe an:
kubectl apply -f 03-baseline-deny.yaml
- Clientverbindung von
client-ns.testen
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'
- Testen Sie die Inferenz vom nicht autorisierten Rogue-Client im nicht vertrauenswürdigen Mandanten:
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'
Zugriff an Entwickler delegieren (NetworkPolicy-Stufe)
Das gemma-llm-Anwendungsteam wendet jetzt eine standardmäßige NetworkPolicy mit Namespace-Bereich an, um autorisierten Traffic von client-ns zuzulassen.
- Erstellen Sie das NetworkPolicy-Manifest
04-app-networkpolicy.yamlfür den Entwickler:
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
- Wenden Sie die Richtlinie an:
kubectl apply -f 04-app-networkpolicy.yaml
- Clientverbindung von
client-nstesten
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'
- Testen Sie die Verbindung vom Rogue-Client in
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. Erzwingung globaler Schutzmaßnahmen bei der Bewertung (Administratorstufe)
Als Clusteradministrator erzwingen Sie jetzt eine Administrator-ClusterNetworkPolicy ganz oben in der GKE-Auswertungsreihenfolge. Diese Stufe wird zuerst ausgewertet. Jede übereinstimmende Regel hat sofort Vorrang vor allen anderen Stufen. Wenn keine Übereinstimmung gefunden wird, wird das Paket zur Auswertung an die niedrigeren Stufen (NetworkPolicy und Baseline) weitergeleitet.
In diesem Schritt wenden Sie eine Richtlinie an, die:
- Clusterweite Guardrail für ausgehenden Traffic: Blockiert alle Pods in jedem Namespace, damit sie den Google Cloud-Metadatenserver (
169.254.169.254/32) nicht erreichen können. - DNS-Unterstützung: Lässt den gesamten DNS-Traffic auf Port 53 zu, damit Pods Hostnamen auflösen können (unter Berücksichtigung der lokalen DNS-Caches von GKE).
- Delegierung von eingehendem und ausgehendem Traffic: Alle anderen Traffic-Entscheidungen werden mit der Aktion „Pass“ weitergegeben, sodass Standard-Entwicklernetzwerkrichtlinien und Standard-Ablehnungsregeln die endgültigen Entscheidungen treffen können.
- Erstellen Sie das Manifest für die Richtlinie der
05-admin-guardrails.yaml-Administratorebene, die auf alle Namespaces ausgerichtet ist.
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
- Wenden Sie die Richtlinie für die Administratorstufe an:
kubectl apply -f 05-admin-guardrails.yaml
- Testen Sie die Exfiltration von Metadaten in allen drei Namespaces:
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)"
- Clientverbindung von
client-nstesten
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'
- Simulieren Sie einen Umgehungsversuch eines Entwicklers, indem Sie eine NetworkPolicy für ausgehenden Traffic in gemma-llm anwenden, die alles zulässt:
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
- Testen Sie, ob die Entwicklerumgehung den Zugriff auf Metadaten freigeben kann:
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. Interaktive Überprüfung mit Hubble und Cloud Logging
Um die Richtlinienerzwingung in Echtzeit zu beobachten und Paketentscheidungen in Ihrem Cluster zu prüfen, verwenden Sie eine Cloud Shell-Sitzung mit zwei Bereichen, die eBPF-Paket-Tracing auf Kernelebene mit automatisierter Testausführung kombiniert.
- Klicken Sie im Cloud Shell-Menü auf das Pluszeichen, um einen zweiten Terminal-Tab zu öffnen.
- Führen Sie in diesem neuen Tab die folgenden Befehle aus, um den Alias zu erstellen und den Live-Traffic für den Namespace
gemma-llmzu beobachten:
# 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
- Wechseln Sie zurück zum ersten Cloud Shell-Tab und prüfen Sie, ob Sie sich im Verzeichnis
gke-cnp-labbefinden.
cd ~/gke-cnp-lab
- Führen Sie den folgenden Block aus, um das automatisierte Überprüfungsskript zu erstellen und auszuführen. Dieses Script führt die erforderlichen Traffic-Tests aus und fragt Cloud Logging mit dem genauen Metadatenschema der GKE auf Stammebene ab, um die genauen Dataplane V2-Regeln anzuzeigen, die die Anfragen ausgewertet haben:
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
- Während das Script im ersten Tab ausgeführt wird, können Sie die Hubble-Ausgabe im zweiten Tab beobachten.
In Hubble (Tab 2): Die autorisierten HTTP-Anfragen werden grün als WEITERGELEITET angezeigt. Für den blockierten Traffic werden rote Ereignisse (DROPPED) angezeigt. Der Rogue-Mandant: Die Verbindung wird von der Admin-Ebene weitergegeben und auf der Baseline-Ebene blockiert. Die Metadatenabfrage: Die Anfrage zum Abrufen von Cloud-Anmeldedaten wird sofort auf der Administratorebene blockiert.

In Cloud Logging (Tab 1): Das Script wird beendet, indem die genauen Audit-Datensätze abgerufen werden, die an Google Cloud Logging gesendet wurden. Darin wird explizit nachgewiesen, welche Stufe (Admin oder Baseline) und welche Richtlinie für das Stoppen des nicht autorisierten Traffics verantwortlich war. Die Zeitstempel sind lokalisiert und berücksichtigen die Zeitzone.

10. Bereinigen
- Zweiten Tab schließen
- Wechseln Sie zurück zum ersten Cloud Shell-Tab.
- Löschen Sie die bereitgestellten Kubernetes-Manifeste, Namespaces und Secrets in der folgenden Reihenfolge:
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
- Bereinigen Sie die Infrastruktur mit dem folgenden Befehl und geben Sie zur Bestätigung
yesein:
terraform destroy --auto-approve
- Prüfen Sie, ob alle Ressourcen in Ihrem aktiven Projekt ordnungsgemäß beendet wurden:
gcloud container clusters list --region=us-west4
gcloud compute networks list
- Löschen Sie das Verzeichnis
gke-cnp-labund seinen Inhalt:
cd
rm -r gke-cnp-lab
11. Glückwunsch
Sie haben das Codelab „KI-Inferenzarbeitslasten mit GKE ClusterNetworkPolicy sichern“ erfolgreich abgeschlossen.
In diesem praxisorientierten Lab haben Sie eine robuste, mehrstufige Defense-in-Depth-Netzwerkarchitektur für die Bereitstellung sensibler privater LLM-Arbeitslasten in der Google Kubernetes Engine entworfen und validiert.
Was Sie erreicht haben:
- Infrastruktur als Code: Mit Terraform wurden ein hochsicherer privater GKE Standard-Cluster, ein privates regionales Subnetz und dedizierte Cloud TPU v5e-Hardware dynamisch bereitgestellt.
- Telemetrie und Beobachtbarkeit: Konfiguration von Dataplane V2-Netzwerkprotokollierung zusammen mit eBPF-Hubble-Tracing auf Kernel-Ebene, um eine detaillierte Sichtbarkeit von Paketen zur Laufzeit zu erreichen.
- Hierarchischer Sicherheitsstatus: Beispiel für eine klare Trennung der operativen Aufgaben in drei verschiedene Bewertungsebenen:
- Admin-Stufe: Clusterweite, nicht umgehbare Schutzmaßnahmen für ausgehenden Traffic, die den lateralen Zugriff auf Metadaten-Anmeldedaten (169.254.169.254) blockieren.
- NetworkPolicy-Stufe: Delegierte Namespace-bezogene Ingress-Grenzen für Anwendungsentwickler, um Produktionsclientverbindungen sicher zu autorisieren.
- Baseline-Stufe: Ein clusterweites Fallback-Sicherheitsnetz, das unautorisierten Mandantentraffic standardmäßig ablehnt.
- Echtzeitprüfung: Führen Sie eine vollständige Test-Suite zur Verifizierung aus, beobachten Sie Paketverluste in Echtzeit auf Linux-Kernel-Ebene und fragen Sie strukturierte Richtlinienaktionsprüfungen direkt in Google Cloud Logging ab.
Weitere Informationen
Weitere Informationen zum GKE-Netzwerk