使用 GKE ClusterNetworkPolicy 保护工作负载 - AI 推理示例(Gemma、TPU)

1. 概览

本实验将向您介绍 Google Kubernetes Engine (GKE) 上的 ClusterNetworkPolicy。ClusterNetworkPolicy 适用于各种用例,可在整个集群范围内应用,并在管理员层和基准层强制执行。在此示例中,我们将演示如何使用 TPU、vLLM 和验证在 GKE 上通过 LLM 部署 (Gemma) 进行控制。

您将使用以下技术:

角色和用户画像

网络政策 API 围绕特定的组织角色进行构建:

  • 集群管理员使用 (ClusterNetworkPolicy):在管理员和基准层级强制执行集群安全防护措施。
  • 命名空间管理员使用 (NetworkPolicy):管理特定命名空间内的租户级边界和隔离。
  • 应用开发者使用 (NetworkPolicy):为特定于应用的微服务定义精细规则。

为了配置此环境,您将部署一个自定义专用 VPC、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-tier ClusterNetworkPolicy,以默认拒绝未经授权的跨命名空间流量。
  • 应用开发者管理的 NetworkPolicy,将访问权限委托给授权客户端。
  • 应用 Admin-tier ClusterNetworkPolicy,以强制执行会覆盖开发者设置的全局出站流量保护措施。
  • 使用 Hubble eBPF 流和 Cloud Logging 验证实时丢包和通过情况。

在本实验中,您将创建以下模式。

图 1. dbe5e5f7b35afb8e.jpeg

2. Google Cloud 服务设置

自定进度的环境设置

  1. 登录 Google Cloud 控制台,然后创建一个新项目或重复使用现有项目。如果您还没有 Gmail 或 Google Workspace 账号,则必须创建一个。

295004821bab6a87.png37d264871000675d.png96d86d3d5655cdbe.png

  • 项目名称是此项目参与者的显示名称。它是 Google API 尚未使用的字符串。您可以随时对其进行更新。
  • 项目 ID 在所有 Google Cloud 项目中是唯一的,并且是不可变的(一经设置便无法更改)。Cloud 控制台会自动生成一个唯一字符串;通常情况下,您无需关注该字符串。在大多数 Codelab 中,您都需要引用项目 ID(通常用 PROJECT_ID 标识)。如果您不喜欢生成的 ID,可以再随机生成一个 ID。或者,您也可以尝试自己的项目 ID,看看是否可用。完成此步骤后便无法更改该 ID,并且此 ID 在项目期间会一直保留。
  • 此外,还有第三个值,即部分 API 使用的项目编号,供您参考。如需详细了解所有这三个值,请参阅文档。
  1. 接下来,您需要在 Cloud 控制台中启用结算功能,以便使用 Cloud 资源/API。运行此 Codelab 应该不会产生太多的费用(如果有的话)。若要关闭资源以避免产生超出本教程范围的结算费用,您可以删除自己创建的资源或删除项目。Google Cloud 新用户符合参与 300 美元免费试用计划的条件。

启动 Cloud Shell

虽然可以通过笔记本电脑对 Google Cloud 进行远程操作,但在此 Codelab 中,您将使用 Google Cloud Shell,这是一个在云端运行的命令行环境。

在 Google Cloud 控制台 中,点击右上角工具栏中的 Cloud Shell 图标:

激活 Cloud Shell

预配和连接到环境应该只需要片刻时间。完成后,您应该会看到如下内容:

Google Cloud Shell 终端的屏幕截图,显示环境已连接

这个虚拟机已加载了您需要的所有开发者工具。它提供了一个持久的 5 GB 主目录,并且在 Google Cloud 中运行,大大增强了网络性能和身份验证功能。您在此 Codelab 中的所有工作都可以在浏览器中完成。您无需安装任何程序。

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

  • Hugging Face 令牌:需要访问令牌才能下载 Gemma 模型权重。
  • 根据需要接受任何使用协议。

我们将创建一个具有防火墙规则和子网的自定义 VPC,然后创建一个 Autopilot 集群。打开 Cloud 控制台,然后选择您要使用的项目。

  1. 打开 Cloud Shell(位于控制台右上角),确保在 Cloud Shell 中看到正确的项目 ID,并确认所有允许访问的提示。b51b80043d3bac90.png
  2. 创建名为 gke-cnp-lab, 的目录,然后切换到该目录。
mkdir -p gke-cnp-lab && cd gke-cnp-lab
  1. 现在,创建一些变量。注意:请修改 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"
  1. 将配置变量保存到 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
  1. 现在,添加一些配置文件。这些命令将在 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
  1. 确保您位于 gke-cnp-lab 目录中,然后运行以下命令
    terraform init 初始化工作目录。这是第一步,它会下载给定配置所需的提供程序。
    terraform plan -out 会生成执行计划,显示 Terraform 将采取哪些操作来部署您的基础架构。-out 允许您将执行计划保存到命名的二进制文件中。您可以在不进行任何更改的情况下查看会发生什么。
    terraform apply 会运行更新。
terraform init 
terraform plan -out cnpgke 
  1. 应用执行计划。由于您应用的是已保存的执行计划,因此该计划会立即执行,而不会提示您进行确认(这可能需要 6-10 分钟)
terraform apply cnpgke
  1. 验证基础架构部署:
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

8b187e355548b7c5.png

4. 配置网络日志记录

在部署工作负载和政策之前,请先配置 GKE 网络日志记录,以便在 Cloud Logging 中记录连接遥测数据和审核政策决策。

  1. 在 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
  1. 将配置应用于集群
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)。
  1. 在 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}
  1. 使用 vllm-tpu 创建针对 Cloud TPU v5e 的 Gemma 4 推理工作负载清单:
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
  1. 创建部署。
kubectl apply -f 02-gemma4-tpu-vllm.yaml
  1. 如需监控完成状态,请运行以下脚本。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

d4cf83398c69b6b8.png

我们的测试流程

===================================================
           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 中,所有跨命名空间流量都会被隐式允许。

  1. 在 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'
  1. 在 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'
  1. 测试所有三个命名空间中的 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,以建立集群范围内的后备默认拒绝状态。任何未获得更高层级明确允许的跨命名空间通信都将被丢弃。

建立零信任安全状况(基准层级)

  1. 创建 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
  1. 应用基准层级政策:
kubectl apply -f 03-baseline-deny.yaml
  1. 测试来自 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'
  1. 测试来自 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 的授权流量。

  1. 创建开发者 04-app-networkpolicy.yaml NetworkPolicy 清单:
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
  1. 应用政策:
kubectl apply -f 04-app-networkpolicy.yaml
  1. 测试来自 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'
  1. 测试来自 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 缓存)。
  • 入站和出站流量委托:使用“通过”操作将所有其他流量决策向下传递,以便标准开发者网络政策和基准默认拒绝规则可以做出最终决策。
  1. 创建针对所有命名空间的 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
  1. 应用管理员层级政策:
kubectl apply -f 05-admin-guardrails.yaml
  1. 测试所有三个命名空间中的元数据渗漏:
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)"
  1. 测试来自 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'
  1. 在 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
  1. 测试开发者绕过功能是否可以解除元数据访问限制:
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 数据包跟踪与自动化测试执行相结合。

  1. 点击 Cloud Shell 菜单中的“+”图标,打开第二个终端标签页。
  2. 在此新标签页中,运行以下命令以创建别名并开始观察 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
  1. 切换回第一个 Cloud Shell 标签页,并确保您位于 gke-cnp-lab 目录中
cd ~/gke-cnp-lab
  1. 运行以下代码块,创建并执行自动验证脚本。此脚本会运行所需的流量测试,并使用 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
  1. 当脚本在第一个标签页中执行时,请在第二个标签页中观察 Hubble 输出。

在 Hubble(标签页 2)中:您会看到已获授权的 HTTP 请求被转发(显示为绿色)。对于被屏蔽的流量,您会看到红色 (DROPPED) 事件。流氓租户:连接由管理员层传递,并在基准层被屏蔽。元数据查询:其获取云凭据的请求会在管理员层立即被阻止。

7d68cc0e9b90f820.png

在 Cloud Logging 中(标签页 1):脚本最后会拉取发送到 Google Cloud Logging 的确切审核记录。这会明确证明哪个层级(管理员或基准)和哪项政策负责阻止未经授权的流量,并采用本地化、时区感知的时间戳格式。

b9e48fd473451ca2.png

10. 清理

  1. 关闭第二个标签页
  2. 切换回第一个 Cloud Shell 标签页。
  3. 按顺序删除已部署的 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
  1. 使用以下命令清理基础架构,输入 yes 进行确认
terraform destroy --auto-approve
  1. 验证活动项目中的所有资源是否都已完全终止:
gcloud container clusters list --region=us-west4
gcloud compute networks list
  1. 删除 gke-cnp-lab 目录和内容:
cd
rm -r gke-cnp-lab

11. 恭喜

您已成功完成“使用 GKE ClusterNetworkPolicy 保护 AI 推理工作负载”Codelab!

通过完成本实操实验,您设计并验证了一个强大的多层纵深防御网络架构,用于在 Google Kubernetes Engine 上运行敏感的私有 LLM 工作负载。

您已完成的操作:

  • 基础设施即代码:使用 Terraform 动态预配了高度安全的专用 Standard GKE 集群、专用区域级子网和专用 Cloud TPU v5e 硬件。
  • 遥测和可观测性:配置了 Dataplane V2 网络日志记录以及内核级 eBPF Hubble 跟踪,以实现深度运行时数据包可见性。
  • 分层安全状况:例如,在三个不同的评估层级之间明确划分运营职责:
  • 管理员层级:在整个集群范围内强制执行、不可绕过的出站流量防护机制,可阻止横向元数据凭证访问 (169.254.169.254)。
  • NetworkPolicy 层级:将命名空间范围的入站流量边界委托给应用开发者,以安全地授权生产客户端连接。
  • 基准层级:构建了集群范围的后备安全网,默认拒绝未经授权的租户流量。
  • 实时审核:运行完整的验证测试套件,在 Linux 内核级别监控实时丢包情况,并直接在 Google Cloud Logging 中查询结构化政策操作审核

后续步骤/了解详情

您可以详细了解 GKE 网络