1. Przegląd
W tym module poznasz ClusterNetworkPolicy w Google Kubernetes Engine (GKE). Zasady ClusterNetworkPolicy są stosowane w różnych przypadkach użycia i mogą być stosowane w całym klastrze oraz egzekwowane na poziomie administratora i na poziomie podstawowym. W tym przykładzie pokażemy, jak kontrolować wdrożenie LLM (Gemma) w GKE z TPU, vLLM i walidacją.
Będziesz pracować z tymi technologiami:
- Google Kubernetes Engine (GKE) – podstawowa platforma do administrowania kontenerami, która umożliwia hostowanie prywatnych, skalowalnych zbiorów zadań.
- ClusterNetworkPolicy (CNP) – wielopoziomowe zasady sieciowe na poziomie klastra, które umożliwiają stosowanie zabezpieczeń SecOps na poziomach Admin, NetworkPolicy i Baseline.
- Silnik wnioskowania vLLM TPU – wnioskowanie o wysokiej przepustowości obsługujące lekki model Gemma 4 (google/gemma-4-E2B-it) na jednostkach TPU Google z punktami końcowymi REST zgodnymi z OpenAI.
Role i osoby
Interfejs API zasad sieciowych jest oparty na określonych personach organizacyjnych:
- Użycie przez administratora klastra (
ClusterNetworkPolicy): wymusza zabezpieczenia w klastrach na poziomie administratora i podstawowym. - Administrator przestrzeni nazw (
NetworkPolicy): zarządza granicami i izolacją na poziomie najemcy w określonych przestrzeniach nazw. - Zastosowanie przez dewelopera aplikacji (
NetworkPolicy): definiuje szczegółowe reguły dla mikroserwisów aplikacji.
Aby skonfigurować to środowisko, wdrożysz niestandardową prywatną sieć VPC, Cloud NAT i prywatny klaster GKE z dedykowaną pulą węzłów TPU.
Aby zabezpieczyć klaster, utworzysz wielopoziomowe zasoby ClusterNetworkPolicy wraz ze standardowymi obiektami NetworkPolicy, aby wymusić ścisłe zabezpieczenia sieci. Następnie wdrożysz zadanie AI,które używa vLLM i Google Cloud TPU do udostępniania modelu Gemma 4.
Na koniec ustalisz działający punkt odniesienia, zastosujesz zasady zabezpieczeń i skonfigurujesz NetworkLogging za pomocą Cloud Logging i strumieni obserwowalności Hubble eBPF na żywo, aby przeprowadzać audyt decyzji dotyczących zgodności z zasadami i weryfikować odrzucanie pakietów. Wszystkie konfiguracje będą używać kombinacji Terraform, gcloud i kubectl.
W tym module nauczysz się, jak:
- udostępnić sieć prywatną, klaster GKE i akceleratory Cloud TPU v5e za pomocą Terraform;
- Skonfiguruj GKE NetworkLogging, aby rejestrować dane telemetryczne połączenia.
- Wdróż model Gemma 4 E2B na TPU przy użyciu oficjalnego obrazu kontenera vllm/vllm-tpu.
- Przed zastosowaniem ograniczeń ustal działający punkt odniesienia, który potwierdza połączenie między punktami końcowymi.
- Zastosuj zasadę ClusterNetworkPolicy
Baseline-tier, aby domyślnie odrzucać nieautoryzowany ruch między przestrzeniami nazw. - Zastosuj zasadę NetworkPolicy zarządzaną przez dewelopera, aby przekazać dostęp autoryzowanym klientom.
- Zastosuj zasadę
Admin-tierClusterNetworkPolicy, aby wymusić globalne zabezpieczenia ruchu wychodzącego, które zastępują ustawienia dewelopera. - Weryfikowanie odrzuconych i przekazanych pakietów na żywo (za pomocą strumieni eBPF Hubble i Cloud Logging).
W tym module utworzysz ten wzorzec.
Rysunek 1. 
2. Konfiguracja usług Google Cloud
Samodzielne konfigurowanie środowiska
- Zaloguj się w konsoli Google Cloud i utwórz nowy projekt lub użyj istniejącego. Jeśli nie masz jeszcze konta Gmail lub Google Workspace, musisz je utworzyć.



- Nazwa projektu to wyświetlana nazwa dla uczestników tego projektu. Jest to ciąg znaków, który nie jest używany przez interfejsy API Google. Zawsze możesz go zaktualizować.
- Identyfikator projektu jest unikalny we wszystkich projektach Google Cloud i nie można go zmienić po ustawieniu. Konsola Cloud automatycznie generuje unikalny ciąg znaków, który zwykle nie ma znaczenia. W większości modułów codelab musisz odwołać się do identyfikatora projektu (zwykle identyfikowanego jako
PROJECT_ID). Jeśli wygenerowany identyfikator Ci nie odpowiada, możesz wygenerować inny losowy identyfikator. Możesz też spróbować użyć własnej nazwy i sprawdzić, czy jest dostępna. Nie można go zmienić po tym kroku i pozostaje on niezmieniony przez cały czas trwania projektu. - Dla informacji: istnieje trzecia wartość, numer projektu, której używają niektóre interfejsy API. Więcej informacji o wszystkich 3 wartościach znajdziesz w dokumentacji.
- Następnie musisz włączyć płatności w konsoli Cloud, aby móc korzystać z zasobów i interfejsów API Google Cloud. Wykonanie tego ćwiczenia w programowaniu nie będzie kosztować dużo, jeśli w ogóle. Aby wyłączyć zasoby i uniknąć naliczania opłat po zakończeniu tego samouczka, możesz usunąć utworzone zasoby lub projekt. Nowi użytkownicy Google Cloud mogą skorzystać z bezpłatnego okresu próbnego, w którym mają do dyspozycji środki w wysokości 300 USD.
Uruchamianie Cloud Shell
Chociaż Google Cloud można obsługiwać zdalnie z laptopa, w tym ćwiczeniu wykorzystasz Google Cloud Shell, czyli środowisko wiersza poleceń działające w chmurze.
W konsoli Google Cloud kliknij ikonę Cloud Shell na pasku narzędzi w prawym górnym rogu:

Uzyskanie dostępu do środowiska i połączenie się z nim powinno zająć tylko kilka chwil. Po zakończeniu powinno wyświetlić się coś takiego:

Ta maszyna wirtualna zawiera wszystkie potrzebne narzędzia dla programistów. Zawiera również stały katalog domowy o pojemności 5 GB i działa w Google Cloud, co znacznie zwiększa wydajność sieci i usprawnia proces uwierzytelniania. Wszystkie zadania w tym ćwiczeniu możesz wykonać w przeglądarce. Nie musisz niczego instalować.
3. Konfigurowanie środowiska za pomocą Terraform
Do ukończenia tego modułu
- Musisz mieć dostęp do limitu TPU v5e w wybranym regionie i strefie, np. (
us-central1-a, us-west4-a, us-south1-a, us-west1-c, europe-west4-b). Aby uzyskać dostęp, postępuj zgodnie z dokumentem dotyczącym planu TPU i włącz limit TPU. - Jest to małe wdrożenie, które wymaga 4 układów TPU v5e (
ct5lp-hightpu-4t),), które utworzą wycinek 2 x 2 w jednej strefie.
Aby używać Hugging Face
- Token Hugging Face: do pobrania wag modelu Gemma wymagany jest token dostępu.
- Zaakceptuj umowę dotyczącą korzystania z usługi, jeśli jest to wymagane.
Utworzymy niestandardową sieć VPC z regułami zapory sieciowej, podsiecią i klastrem Autopilota. Otwórz konsolę Cloud i wybierz projekt, którego będziesz używać.
- Otwórz Cloud Shell w prawym górnym rogu konsoli i sprawdź, czy widzisz w nim prawidłowy identyfikator projektu. Potwierdź wszystkie prośby o zezwolenie na dostęp.

- Utwórz katalog o nazwie
gke-cnp-lab,i przejdź do niego.
mkdir -p gke-cnp-lab && cd gke-cnp-lab
- Teraz utwórz kilka zmiennych. Uwaga: zmień wartości REGION i ZONE, aby odzwierciedlały rzeczywistą lokalizację, w której masz dostęp do zasobów TPU. Domyślny region używany w module to
us-west4, a strefa tous-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"
- Zapisz zmienne konfiguracji w skrypcie pomocniczym w katalogu gke-cnp-lab. Jeśli sesja Cloud Shell zostanie rozłączona lub zresetowana, możesz szybko przywrócić aktywne zmienne, uruchamiając polecenie
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
- Teraz dodaj kilka plików konfiguracji. Spowoduje to utworzenie plików terraform.tfvars, variables.tf i main.tf w katalogu 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
- Upewnij się, że jesteś w katalogu gke-cnp-lab, i uruchom te polecenia:
terraform initInicjuje katalog roboczy. To pierwszy krok, który polega na pobraniu dostawców wymaganych dla danej konfiguracji.terraform plan -outgeneruje plan wykonania, który pokazuje, jakie działania Terraform podejmie w celu wdrożenia infrastruktury.-outumożliwia zapisanie planu wykonania w nazwanym pliku binarnym. Możesz zobaczyć, co się stanie, bez wprowadzania żadnych zmian.terraform applyprzeprowadza aktualizacje.
terraform init
terraform plan -out cnpgke
- Zastosuj plan wykonania. Ponieważ stosujesz zapisany plan wykonania, zostanie on wykonany natychmiast bez prośby o potwierdzenie (może to potrwać 6–10 minut).
terraform apply cnpgke
- Sprawdź wdrożenie infrastruktury:
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. Konfigurowanie dziennika sieci
Przed wdrożeniem zbiorów zadań i zasad skonfiguruj logowanie sieci GKE, aby rejestrować dane telemetryczne połączeń i decyzje dotyczące zasad kontroli w Cloud Logging.
- W katalogu
gke-cnp-labutwórz plik manifestu konfiguracji 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
- Zastosowanie konfiguracji do klastra
kubectl apply -f 00-network-logging.yaml
kubectl get networklogging default
Uwaga: może pojawić się ostrzeżenie, że w zasobie brakuje kubectl.kubernetes.io/last-applied-configuration annotation.. Jest to całkowicie normalne i oczekiwane. GKE wstępnie tworzy domyślny obiekt NetworkLogging podczas uruchamiania klastra, a polecenie kubectl apply po prostu przejmuje zarządzanie nim, aby zastosować nowe reguły.
5. Wdrażanie zbioru zadań Gemma 4 i sond klienta
Utworzysz odizolowane przestrzenie nazw, aby przetestować wielopoziomowe oceny zasad:
gemma-llm: zawiera główne zadanie wnioskowania Gemma 4 (tier: ai-core).client-ns: zawiera autoryzowanego klienta czatu RAG (tier: app-frontend).untrusted-tenant: zawiera nieautoryzowany kontener sondy (tier: untrusted).
- W
gke-cnp-lab directory,utwórz plik manifestu01-namespaces-clients.yamlzawierający przestrzenie nazw i pody klienta testowego:
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
- Zastosuj przestrzenie nazw i wdrożenia klienta:
kubectl apply -f 01-namespaces-clients.yaml
kubectl get namespaces gemma-llm client-ns untrusted-tenant --show-labels
Wdrażanie zbioru zadań vLLM i Gemma
Utwórz obiekt tajny Hugging Face. Zastąp YOUR_ACTUAL_HUGGING_FACE_TOKEN swoim tokenem:
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}
- Utwórz plik manifestu zbioru zadań wnioskowania Gemma 4 kierowany na Cloud TPU v5e za pomocą 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
- Utwórz wdrożenie.
kubectl apply -f 02-gemma4-tpu-vllm.yaml
- Aby sprawdzić stan ukończenia, uruchom poniższy skrypt. Pody będą czekać na udostępnienie węzła, zanim będą mogły kontynuować. Może to potrwać ponad 13 minut.
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. Omówienie oceny zasad i stanu domyślnego
Zanim zastosujesz zasady sieciowe, dowiedz się, jak zasady ClusterNetworkPolicy GKE i standardowe zasady NetworkPolicy Kubernetes współdziałają na różnych poziomach oceny.
Ruch w GKE jest oceniany za pomocą wielopoziomowego modelu pierwszeństwa:
Admin Tier ⟶ NetworkPolicy Tier ⟶ Baseline Tier ⟶ Default GKE Behavior

Nasz proces testowy
===================================================
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
===================================================
Ustalenie podstawy (test przed wprowadzeniem zasad)
Sprawdź, czy model Gemma 4 działa prawidłowo i obsługuje żądania wnioskowania. W standardowym Kubernetesie bez zasad cały ruch między przestrzeniami nazw jest domyślnie dozwolony.
- Przetestuj wnioskowanie z autoryzowanego klienta w
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'
- Przetestuj wnioskowanie z nieautoryzowanego klienta w
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'
- Sprawdź, czy pody we wszystkich 3 przestrzeniach nazw mogą połączyć się z serwerem metadanych (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. Testowanie oceny – poziom bazowy i poziom zasad sieciowych
W tym kroku jako administrator klastra zastosujesz poziom BaselineClusterNetworkPolicy, aby ustanowić domyślną zasadę odmowy w całym klastrze. Każda komunikacja między przestrzeniami nazw, która nie jest wyraźnie dozwolona przez wyższy poziom, zostanie odrzucona.
Ustanawianie stanu „zero zaufania” (poziom podstawowy)
- Utwórz plik manifestu zasady
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
- Zastosuj zasadę poziomu Baseline:
kubectl apply -f 03-baseline-deny.yaml
- Testowanie połączenia klienta z:
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'
- Przetestuj wnioskowanie z nieautoryzowanego klienta w niezaufanej dzierżawie:
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'
Przekazywanie dostępu deweloperom (typ NetworkPolicy)
Zespół aplikacji gemma-llm zastosuje teraz standardową zasadę NetworkPolicy o zakresie przestrzeni nazw, aby zezwolić na autoryzowany ruch z client-ns.
- Utwórz plik manifestu NetworkPolicy dla programisty
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
- Zastosuj zasadę:
kubectl apply -f 04-app-networkpolicy.yaml
- Testowanie połączenia klienta z:
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'
- Przetestuj połączenie z nieautoryzowanego klienta w
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. Ocena wymusza globalne ograniczenia (poziom administratora)
Jako administrator klastra wymusisz teraz poziom administratoraClusterNetworkPolicy na samej górze kolejności oceny GKE. Ten poziom jest oceniany jako pierwszy. Każda pasująca reguła ma natychmiastowe pierwszeństwo przed wszystkimi innymi poziomami. Jeśli nie ma dopasowania, pakiet jest przekazywany do oceny przez niższe poziomy (NetworkPolicy i Baseline).
W tym kroku zastosujesz zasadę, która:
- Zabezpieczenie ruchu wychodzącego w całym klastrze: blokuje wszystkim podom we wszystkich przestrzeniach nazw dostęp do serwera metadanych Google Cloud (
169.254.169.254/32). - Obsługa DNS: zezwala na cały ruch DNS na porcie 53, dzięki czemu pody mogą rozpoznawać nazwy hostów (z uwzględnieniem lokalnych pamięci podręcznych DNS GKE).
- Delegowanie ruchu przychodzącego i wychodzącego: przekazuje wszystkie inne decyzje dotyczące ruchu za pomocą działania Pass, dzięki czemu ostateczne decyzje mogą podejmować standardowe zasady sieciowe dewelopera i podstawowe reguły domyślnego odrzucania.
- Utwórz plik manifestu zasady na poziomie administratora
05-admin-guardrails.yamlkierowanej na wszystkie przestrzenie nazw
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
- Zastosuj zasadę poziomu administratora:
kubectl apply -f 05-admin-guardrails.yaml
- Przetestuj wydobycie metadanych we wszystkich 3 przestrzeniach nazw:
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)"
- Testowanie połączenia klienta z:
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'
- Zasymuluj próbę obejścia zabezpieczeń przez dewelopera, stosując regułę NetworkPolicy zezwalającą na cały ruch wychodzący w 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
- Sprawdź, czy obejście dla deweloperów może odblokować dostęp do metadanych:
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. Interaktywna weryfikacja za pomocą Hubble i Cloud Logging
Aby obserwować egzekwowanie zasad w czasie rzeczywistym i kontrolować decyzje dotyczące pakietów w klastrze, użyjesz sesji Cloud Shell z podwójnym panelem, która łączy śledzenie pakietów eBPF na poziomie jądra z automatycznym wykonywaniem testów.
- Kliknij ikonę „+” w menu Cloud Shell, aby otworzyć drugą kartę terminala.
- Na nowej karcie uruchom te polecenia, aby utworzyć alias i rozpocząć obserwowanie ruchu na żywo w przestrzeni nazw
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
- Wróć na pierwszą kartę Cloud Shell i upewnij się, że jesteś w katalogu
gke-cnp-lab.
cd ~/gke-cnp-lab
- Uruchom ten blok, aby utworzyć i wykonać skrypt automatycznej weryfikacji. Ten skrypt uruchamia wymagane testy ruchu i wysyła zapytania do Cloud Logging przy użyciu dokładnego schematu metadanych na poziomie głównym GKE, aby wyświetlić dokładne reguły Dataplane V2, które oceniły żądania:
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
- Podczas wykonywania skryptu na pierwszej karcie obserwuj dane wyjściowe Hubble na drugiej karcie.
W Hubble (karta 2): zobaczysz autoryzowane żądania HTTP PRZEKAZYWANE na zielono. W przypadku zablokowanego ruchu zobaczysz czerwone zdarzenia (ODRZUCONE). Nieuczciwy najemca: połączenie jest przekazywane przez poziom administratora i blokowane na poziomie podstawowym. Zapytanie o metadane: żądanie dostępu do danych logowania w chmurze jest natychmiast blokowane na poziomie administratora.

W Cloud Logging (karta 1): skrypt kończy działanie, pobierając dokładne rekordy kontrolne wysłane do Cloud Logging. To wyraźnie pokazuje, który poziom (administracyjny lub podstawowy) i która zasada były odpowiedzialne za zatrzymanie nieautoryzowanego ruchu, sformatowane za pomocą zlokalizowanych sygnatur czasowych uwzględniających strefę czasową.

10. Czyszczenie danych
- Zamknij drugą kartę.
- Wróć na pierwszą kartę Cloud Shell.
- Usuń wdrożone obiekty manifestu, przestrzenie nazw i obiekty tajne Kubernetes w tej kolejności:
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
- Wyczyść infrastrukturę za pomocą tego polecenia, wpisz
yes, aby potwierdzić
terraform destroy --auto-approve
- Sprawdź, czy wszystkie zasoby w aktywnym projekcie zostały prawidłowo zakończone:
gcloud container clusters list --region=us-west4
gcloud compute networks list
- Usuń katalog
gke-cnp-labi jego zawartość:
cd
rm -r gke-cnp-lab
11. Gratulacje
Udało Ci się ukończyć ćwiczenie z programowania dotyczące zabezpieczania zbiorów zadań wnioskowania AI za pomocą ClusterNetworkPolicy w GKE.
Po ukończeniu tego laboratorium praktycznego masz już zaprojektowaną i zweryfikowaną solidną, wielopoziomową architekturę sieciową z obroną w głąb, która umożliwia obsługę poufnych prywatnych zbiorów zadań LLM w Google Kubernetes Engine.
Co udało Ci się osiągnąć:
- Infrastruktura jako kod: udostępniono wysoce bezpieczny prywatny klaster GKE w trybie standardowym, prywatną podsieć regionalną i dedykowany sprzęt Cloud TPU v5e dynamicznie przy użyciu Terraform.
- Telemetria i dostrzegalność: skonfigurowano logowanie sieciowe Dataplane V2 wraz ze śledzeniem eBPF Hubble na poziomie jądra, aby uzyskać szczegółowy wgląd w pakiety w czasie działania.
- Hierarchiczny stan zabezpieczeń: np. wyraźny podział obowiązków operacyjnych na 3 odrębne poziomy oceny:
- Poziom administratora: wymuszane w całym klastrze, niepomijalne zabezpieczenia ruchu wychodzącego blokujące boczny dostęp do danych logowania metadanych (169.254.169.254).
- Poziom NetworkPolicy: delegowane granice ruchu przychodzącego w zakresie przestrzeni nazw do deweloperów aplikacji w celu bezpiecznego autoryzowania połączeń klientów produkcyjnych.
- Poziom podstawowy: utworzono sieć bezpieczeństwa na poziomie klastra, która domyślnie odrzuca nieautoryzowany ruch najemców.
- Kontrola w czasie rzeczywistym: uruchom pełny zestaw testów weryfikacyjnych, obserwuj w czasie rzeczywistym odrzucanie pakietów na poziomie jądra systemu Linux i wysyłaj zapytania o ustrukturyzowane kontrole działań związanych z zasadami bezpośrednio w Google Cloud Logging.
Kolejne kroki / Więcej informacji
Więcej informacji o sieci GKE znajdziesz w dokumentacji.
- Szczegółowe omówienie konfigurowania ClusterNetworkPolicy w GKE