1. 總覽
本實驗室將介紹 Google Kubernetes Engine (GKE) 的 ClusterNetworkPolicy。ClusterNetworkPolicy 適用於多種用途,可套用至整個叢集,並在管理員和基準層級強制執行。在本範例中,我們將示範如何使用 TPU、vLLM 和驗證,在 GKE 上控管 LLM 部署作業 (Gemma)。
您將使用下列技術:
- Google Kubernetes Engine (GKE):基礎容器自動化調度管理平台,用於託管私有、可擴充的工作負載。
- ClusterNetworkPolicy (CNP):叢集範圍的多層級網路政策,可在管理員、NetworkPolicy 和基準層級啟用 SecOps 護欄。
- vLLM TPU 推論引擎:在 Google TPU 上,使用與 OpenAI 相容的 REST 端點,為輕量 Gemma 4 模型 (google/gemma-4-E2B-it) 提供高處理量推論服務。
角色和人物
網路政策 API 是圍繞特定組織角色建構而成:
- 叢集管理員用途 (
ClusterNetworkPolicy):在管理員和基準層級,強制執行叢集安全防護措施。 - 命名空間管理員用途 (
NetworkPolicy):管理特定命名空間內的租戶層級邊界和隔離。 - 應用程式開發人員用途 (
NetworkPolicy):為應用程式專屬微服務定義精細規則。
如要設定這個環境,您需要部署自訂私人虛擬私有雲、Cloud NAT,以及具備專屬 TPU 節點集區的私人 GKE 叢集。
為保護叢集,您將編寫多層級 ClusterNetworkPolicy 資源,並搭配標準 NetworkPolicy 物件,強制執行嚴格的網路防護措施。接著,您將部署 AI 工作負載,使用 vLLM 和 Google Cloud TPU 提供 Gemma 4 模型。
最後,您將建立工作基準、套用安全性政策,並使用 Cloud Logging 和即時 Hubble eBPF 觀測能力串流設定 NetworkLogging,藉此稽核這些政策裁決並驗證封包捨棄情形。所有設定都會使用 Terraform、gcloud 和 kubectl. 的組合
在本實驗室中,您將瞭解如何執行下列工作:
- 使用 Terraform 佈建私人網路、GKE 叢集和 Cloud TPU v5e 加速器。
- 設定 GKE NetworkLogging,記錄連線遙測資料。
- 使用官方 vllm/vllm-tpu 容器映像檔,在 TPU 上部署 Gemma 4 E2B。
- 套用限制前,請先建立工作基準,證明端對端連線能力。
- 套用
Baseline-tierClusterNetworkPolicy,預設拒絕未經授權的跨命名空間流量。 - 套用開發人員管理的 NetworkPolicy,將存取權委派給授權用戶端。
- 套用
Admin-tierClusterNetworkPolicy,強制執行全域輸出護欄,覆寫開發人員設定。 - 使用 Hubble eBPF 串流和 Cloud Logging,驗證即時封包捨棄和傳遞。
在本實驗室中,您將建立下列模式。
圖 1:
2. Google Cloud 服務設定
自修實驗室環境設定
- 登入 Google Cloud 控制台,建立新專案或重複使用現有專案。如果您沒有 Gmail 或 Google Workspace 帳戶,請建立帳戶。



- 「專案名稱」是這項專案參與者的顯示名稱。這組字串並非 Google API 所用。你隨時可以更新。
- 專案 ID 在所有 Google Cloud 專案中都是不重複的,且設定後即無法變更。Cloud 控制台會自動產生一組不重複的字串,通常您不需要知道這組字串。在大多數程式碼實驗室中,您必須參照專案 ID (通常會標示為
PROJECT_ID)。如果您不喜歡系統產生的 ID,可以再產生一組隨機 ID。或者,您也可以嘗試使用自己的名稱,看看是否可用。這個步驟完成後即無法變更,且在專案期間會保持不變。 - 此外,還有第三個值,也就是「專案編號」,部分 API 會使用這個值。如要進一步瞭解這三個值,請參閱說明文件。
- 接著,您必須在 Cloud 控制台中啟用計費功能,才能使用 Cloud 資源/API。完成這個程式碼實驗室的費用極低,甚至可能完全免費。如要關閉資源,避免產生本教學課程以外的費用,可以刪除建立的資源或專案。新註冊 Google Cloud 的使用者可參加價值$300 美元的免費試用計畫。
啟動 Cloud Shell
雖然您可以透過筆電遠端操作 Google Cloud,但在本程式碼實驗室中,您將使用 Google Cloud Shell,這是在雲端執行的指令列環境。
在 Google Cloud 控制台中,點選右上角工具列的「Cloud Shell」圖示:

佈建並連線至環境的作業需要一些時間才能完成。完成後,您應該會看到類似下方的內容:

這個虛擬機器已預先安裝所有必要的開發人員工具,提供永久的 5 GB 主目錄,而且在 Google Cloud 中運作,大幅提升網路效能並強化驗證功能。本程式碼實驗室的所有工作都可以在瀏覽器中完成。您不需要安裝任何項目。
3. 使用 Terraform 設定環境
為順利完成這個實驗室,
- 您必須有權存取所選區域和可用區的 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
我們會建立自訂虛擬私有雲,其中包含防火牆規則、子網路和 Autopilot 叢集。開啟 Cloud 控制台,選取要使用的專案。
- 開啟控制台右上方的 Cloud Shell,確認 Cloud Shell 顯示正確的專案 ID,並在出現提示時允許存取。

- 建立名為
gke-cnp-lab,的目錄,然後切換至該目錄。
mkdir -p gke-cnp-lab && cd gke-cnp-lab
- 現在建立一些變數。附註:請編輯 REGION 和 ZONE,反映您有權存取 TPU 資源的實際位置。實驗室預設使用的區域
us-west4和可用區us-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"
- 將設定變數儲存至 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
- 現在新增一些設定檔。這些指令會在 gke-cnp-lab 目錄中建立 terraform.tfvars、variables.tf 和 main.tf 檔案。
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 網路記錄,在 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,中,建立包含命名空間和測試用戶端 Pod 的01-namespaces-clients.yaml資訊清單:
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
部署工作負載 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}
- 使用 vllm-tpu 建立 Gemma 4 推論工作負載資訊清單,並指定 Cloud TPU v5e:
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
- 如要監控完成狀態,請執行下列指令碼。Pod 會等待節點佈建完成,然後才繼續執行,這可能需要 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. 瞭解政策評估和預設狀態
套用網路政策前,請先瞭解 GKE ClusterNetworkPolicy 和標準 Kubernetes NetworkPolicy 如何在不同評估層級互動。
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'
- 測試三個命名空間中的 Pod 是否都能連上中繼資料伺服器 (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. 評估測試基準和網路政策層級
在這個步驟中,您將扮演叢集管理員,套用基準層級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'
- 從 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'
將存取權委派給開發人員 (NetworkPolicy 級別)
gemma-llm 應用程式團隊現在會套用標準命名空間範圍的 NetworkPolicy,允許來自 client-ns 的授權流量。
- 建立開發人員
04-app-networkpolicy.yamlNetworkPolicy 資訊清單:
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. 評估作業強制執行全域防護機制 (管理員層級)
現在,請以叢集管理員的身分,在 GKE 評估順序的最頂層,強制執行管理員層級ClusterNetworkPolicy。系統會先評估這個層級,任何相符的規則都會立即優先於所有其他層級。如果沒有相符項目,封包就會傳遞至較低層級 (NetworkPolicy 和 Baseline) 進行評估。
在這個步驟中,您將套用政策,
- 叢集層級輸出守則:禁止所有命名空間中的 Pod 存取 Google Cloud 中繼資料伺服器 (
169.254.169.254/32)。 - DNS 支援:允許通訊埠 53 上的所有 DNS 流量,讓 Pod 能解析主機名稱 (考量到 GKE 的本機 DNS 快取)。
- 輸入和輸出委派:使用「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'
- 在 gemma-llm 中套用允許所有輸出流量的 NetworkPolicy,模擬開發人員嘗試略過政策:
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
- 執行下列區塊,建立並執行自動驗證指令碼。這個指令碼會執行必要的流量測試,並使用 GKE 的確切根層級中繼資料結構定義查詢 Cloud Logging,顯示評估要求的確切 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 要求以綠色顯示為「FORWARDED」。如果是遭封鎖的流量,則會顯示紅色 (已捨棄) 事件。惡意租戶:連線會由管理員層級向下傳遞,並在基準層級遭到封鎖。中繼資料查詢:存取雲端憑證的要求會立即在管理員層級遭到封鎖。

在 Cloud Logging (分頁 1):指令碼會提取傳送至 Google Cloud Logging 的確切稽核記錄,然後結束。這項資訊會明確指出是哪個層級 (管理員或基準) 和哪項政策阻止了未經授權的流量,並以本地化、時區感知的時間戳記格式呈現。

10. 清除
- 關閉第二個分頁
- 切換回第一個 Cloud Shell 分頁。
- 依序刪除已部署的 Kubernetes 資訊清單、命名空間和 Secret:
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. 恭喜
您已成功完成「使用 GKE ClusterNetworkPolicy 保護 AI 推論工作負載」程式碼實驗室!
完成這個實作實驗室後,您已設計並驗證穩健的多層縱深防禦網路架構,可在 Google Kubernetes Engine 提供機密私人 LLM 工作負載。
您已完成以下事項:
- 基礎架構即程式碼:使用 Terraform 動態佈建高度安全的私人 Standard GKE 叢集、私人區域子網路,以及專屬 Cloud TPU v5e 硬體。
- 遙測和觀測能力:設定 Dataplane V2 網路記錄,並搭配核心層級 eBPF Hubble 追蹤功能,深入掌握執行階段封包。
- 階層式資安態勢:例如,在三個不同的評估層級中,明確區分營運職責:
- 管理員層級:在整個叢集強制執行不可略過的輸出防護措施,封鎖橫向中繼資料憑證存取 (169.254.169.254)。
- NetworkPolicy 層級:將命名空間範圍的輸入邊界委派給應用程式開發人員,安全地授權正式版用戶端連線。
- 基準層級:建立叢集層級的備用安全網,預設拒絕未經授權的租戶流量。
- 即時稽核:執行完整的驗證測試套件、在 Linux 核心層級監控即時封包捨棄情形,以及直接在 Google Cloud Logging 查詢結構化政策動作稽核記錄
後續步驟/瞭解詳情
如要進一步瞭解 GKE 網路,請參閱這篇文章。