Защита рабочих нагрузок с помощью ClusterNetworkPolicy в GKE – пример вывода ИИ (Gemma, TPU)

1. Обзор

В этой лабораторной работе вы познакомитесь с ClusterNetworkPolicy в Google Kubernetes Engine (GKE). ClusterNetworkPolicy применяется в различных сценариях и может применяться на уровне кластера и принудительно на уровнях администратора и базовом. В этом примере мы покажем, как управлять развертыванием LLM (Gemma) в GKE с TPU, vLLM и проверкой.

Вы будете работать со следующими технологиями:

  • Google Kubernetes Engine (GKE) – базовая платформа оркестрации контейнеров для размещения частных масштабируемых рабочих нагрузок.
  • ClusterNetworkPolicy (CNP) – многоуровневые сетевые правила на уровне кластера, которые позволяют SecOps устанавливать ограничения на уровнях Admin, NetworkPolicy и Baseline.
  • vLLM TPU Inference Engine – высокопроизводительный механизм вывода, обслуживающий облегченную модель Gemma 4 (google/gemma-4-E2B-it) на TPU Google с конечными точками REST, совместимыми с OpenAI.

Роли и персонажи

Network Policy API структурирован по ролям в организации:

  • Использование администратором кластера (ClusterNetworkPolicy). Обеспечивает соблюдение мер безопасности в кластерах на уровнях администратора и базовом.
  • Namespace Administrator use (NetworkPolicy): управление границами и изоляцией на уровне клиента в определенных пространствах имен.
  • Использование разработчиком приложений (NetworkPolicy): определяет подробные правила для микросервисов, связанных с приложениями.

Чтобы настроить эту среду, вы развернете пользовательское частное VPC, Cloud NAT и частный кластер GKE с выделенным пулом узлов TPU.

Чтобы защитить кластер, вы создадите многоуровневые ресурсы ClusterNetworkPolicy вместе со стандартными объектами NetworkPolicy, чтобы обеспечить строгое соблюдение сетевых ограничений. Затем вы развернете рабочую нагрузку ИИ,которая использует vLLM и Cloud TPU Google Cloud для обслуживания модели Gemma 4.

Наконец, вы создадите рабочую базовую конфигурацию, примените правила безопасности и настроите NetworkLogging с помощью Cloud Logging и потоков наблюдаемости Hubble eBPF, чтобы проверять решения о нарушении правил и отслеживать отброшенные пакеты. Во всех конфигурациях будут использоваться Terraform, gcloud и kubectl..

В этой практической работе вы научитесь выполнять следующие задачи:

  • Подготовьте частную сеть, кластер GKE и ускорители Cloud TPU v5e с помощью Terraform.
  • Настройте GKE NetworkLogging для записи телеметрии подключений.
  • Разверните Gemma 4 E2B на TPU, используя официальный образ контейнера vllm/vllm-tpu.
  • Прежде чем применять ограничения, установите рабочую базовую конфигурацию, подтверждающую сквозное подключение.
  • Примените Baseline-tier ClusterNetworkPolicy, чтобы по умолчанию запретить неавторизованный трафик между пространствами имен.
  • Примените правило NetworkPolicy, управляемое разработчиком, чтобы делегировать доступ авторизованным клиентам.
  • Примените ClusterNetworkPolicy Admin-tier, чтобы принудительно использовать глобальные ограничения исходящего трафика, которые переопределяют настройки разработчика.
  • Проверьте пропуск и сброс пакетов в реальном времени с помощью потоков Hubble eBPF и Cloud Logging.

В этой лабораторной работе вы создадите следующий шаблон.

Рисунок 1. dbe5e5f7b35afb8e.jpeg

2. Настройка сервисов Google Cloud

Самостоятельная настройка среды

  1. Войдите в консоль Google Cloud и создайте новый проект или выберите существующий. Если у вас нет аккаунта Gmail или Google Workspace, создайте его.

295004821bab6a87.png37d264871000675d.png96d86d3d5655cdbe.png

  • Название проекта – это отображаемое название для участников проекта. Это строка символов, которая не используется API Google. Вы можете изменить его в любое время.
  • Идентификатор проекта уникален для всех проектов Google Cloud и является неизменяемым (его нельзя изменить после установки). Консоль Cloud автоматически генерирует уникальную строку. Обычно ее значение не имеет значения. В большинстве практических работ вам нужно будет ссылаться на идентификатор проекта (обычно обозначаемый как PROJECT_ID). Если вам не нравится сгенерированный идентификатор, вы можете сгенерировать другой случайный. Вы также можете попробовать ввести свой вариант и проверить, доступен ли он. После этого шага его нельзя будет изменить, и оно останется на протяжении всего проекта.
  • Для справки: есть ещё третье значение – номер проекта, который используется некоторыми API. Подробную информацию обо всех трех значениях можно найти в документации.
  1. Затем вам нужно будет включить платежный аккаунт в Cloud Console, чтобы использовать ресурсы и API Cloud. Выполнение этой практической работы не потребует больших затрат, если они вообще будут. Чтобы избежать дополнительных расходов, удалите созданные ресурсы или проект. Новые пользователи Google Cloud могут получить 300 долларов США на бесплатный пробный период.

Запустите Cloud Shell

Хотя Google Cloud можно управлять удаленно с ноутбука, в этой практической работе вы будете использовать Google Cloud Shell – среду командной строки, работающую в облаке.

В консоли Google Cloud нажмите на значок Cloud Shell на панели инструментов в правом верхнем углу.

Активировать Cloud Shell

Подготовка и подключение к среде займет всего несколько минут. После завершения вы увидите примерно следующее:

Скриншот терминала Google Cloud Shell, показывающий, что среда подключена

На этой виртуальной машине установлены все необходимые инструменты разработки. Она предлагает постоянный домашний каталог объемом 5 ГБ и работает на платформе Google Cloud, что значительно повышает производительность сети и аутентификацию. Все действия в рамках этой практической работы можно выполнить в браузере. Ничего устанавливать не нужно.

3. Настройка среды с помощью Terraform

Чтобы выполнить практическую работу,

  • Вам нужен доступ к квоте TPU v5e в выбранном регионе и зоне, например us-central1-a, us-west4-a, us-south1-a, us-west1-c, europe-west4-b. Чтобы получить доступ, следуйте инструкциям в документации по тарифному плану TPU и включите квоту TPU.
  • Это небольшое развертывание, для которого требуется четыре чипа TPU v5e (ct5lp-hightpu-4t),), которые образуют срез 2 x 2 в одной зоне.

Как использовать Hugging Face

  • Токен Hugging Face. Чтобы скачать веса модели Gemma, нужен токен доступа.
  • Примите соглашение об использовании, если это необходимо.

Мы создадим пользовательскую сеть VPC с правилами брандмауэра, подсетью и кластером Autopilot. Откройте Cloud Console и выберите проект, который будете использовать.

  1. Откройте Cloud Shell в правом верхнем углу консоли. Убедитесь, что в Cloud Shell указан правильный идентификатор проекта, и подтвердите все запросы на доступ. 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, создайте манифест 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. Создайте манифест рабочей нагрузки инференса Gemma 4, предназначенный для Cloud TPU v5e, с помощью vllm-tpu:
cat << 'EOF' > 02-gemma4-tpu-vllm.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: gemma-serving
  namespace: gemma-llm
  labels:
    app: gemma-service
spec:
  replicas: 1
  selector:
    matchLabels:
      app: gemma-service
  template:
    metadata:
      labels:
        app: gemma-service
        ai.gke.io/model: gemma4
        ai.gke.io/inference-server: vllm-tpu
    spec:
      tolerations:
        - key: "google.com/tpu"
          operator: "Exists"
          effect: "NoSchedule"
      nodeSelector:
        cloud.google.com/gke-tpu-accelerator: tpu-v5-lite-podslice
        cloud.google.com/gke-tpu-topology: 2x2
      containers:
        - name: vllm-inference
          image: vllm/vllm-tpu:latest
          command: ["python3", "-m", "vllm.entrypoints.openai.api_server"]
          args:
            - "--model=google/gemma-4-E2B-it"
            - "--tensor-parallel-size=4"
            - "--host=0.0.0.0"
            - "--port=8000"
            - "--max-model-len=4096"
            - "--max-num-batched-tokens=4096"
          env:
            - name: HF_TOKEN
              valueFrom:
                secretKeyRef:
                  name: hf-secret
                  key: hf_token
            - name: HUGGING_FACE_HUB_TOKEN
              valueFrom:
                secretKeyRef:
                  name: hf-secret
                  key: hf_token
          volumeMounts:
            - mountPath: /dev/shm
              name: dshm
          ports:
            - name: http-inference
              containerPort: 8000
          resources:
            requests:
              google.com/tpu: "4"
              memory: "32Gi"
              cpu: "8"
            limits:
              google.com/tpu: "4"
              memory: "32Gi"
              cpu: "8"
          startupProbe:
            httpGet:
              path: /health
              port: 8000
            failureThreshold: 120
            periodSeconds: 10
          readinessProbe:
            httpGet:
              path: /health
              port: 8000
            periodSeconds: 5
      volumes:
        - name: dshm
          emptyDir:
            medium: Memory
---
apiVersion: v1
kind: Service
metadata:
  name: gemma-service
  namespace: gemma-llm
spec:
  type: ClusterIP
  selector:
    app: gemma-service
  ports:
    - name: http
      protocol: TCP
      port: 8000
      targetPort: 8000
EOF
  1. Создайте развертывание.
kubectl apply -f 02-gemma4-tpu-vllm.yaml
  1. Чтобы отслеживать статус выполнения, запустите следующий скрипт: Поды будут ждать, пока узел не будет подготовлен, прежде чем продолжить. Это может занять более 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 и стандартные правила NetworkPolicy в Kubernetes взаимодействуют на разных уровнях оценки.

Трафик в 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. Проверьте, могут ли поды во всех трех пространствах имен подключиться к серверу метаданных (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. Протестируйте инференс из неавторизованного клиента-мошенника в ненадежном клиенте:
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. Создайте манифест NetworkPolicy для разработчиков (04-app-networkpolicy.yaml):
cat << 'EOF' > 04-app-networkpolicy.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-rag-client
  namespace: gemma-llm
spec:
  podSelector:
    matchLabels:
      app: gemma-service
  policyTypes:
    - Ingress
  ingress:
    # Explicitly permit ingress only from pods matching app: rag-agent in client-ns
    - from:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: client-ns
          podSelector:
            matchLabels:
              app: rag-agent
      ports:
        - protocol: TCP
          port: 8000
EOF
  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. Оценка применяет глобальные ограничения (уровень администратора)

Выступая в роли администратора кластера,вы теперь будете применять уровень администратораClusterNetworkPolicy в самом верху порядка оценки GKE. Этот уровень оценивается в первую очередь. Любое соответствующее правило имеет приоритет над всеми остальными уровнями. Если совпадений нет, пакет передается на проверку на более низких уровнях (NetworkPolicy и Baseline).

На этом шаге вы примените правило, которое:

  • Ограничение исходящего трафика на уровне кластера. Блокирует доступ всех подов во всех пространствах имен к серверу метаданных Google Cloud (169.254.169.254/32).
  • Поддержка DNS. Разрешает весь трафик DNS через порт 53, чтобы поды могли разрешать имена хостов (с учетом локальных кешей DNS GKE).
  • Делегирование входящего и исходящего трафика. Передает все остальные решения о трафике вниз с помощью действия Pass, чтобы стандартные сетевые политики разработчика и базовые правила запрета по умолчанию могли принимать окончательные решения.
  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. Сымитируйте попытку разработчика обойти защиту, применив разрешающую все исходящие подключения политику NetworkPolicy в gemma-llm:
cat << 'EOF' > 06-bypass-attempt.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: dev-allow-all-egress
  namespace: gemma-llm
spec:
  podSelector: {}
  policyTypes:
    - Egress
  egress:
    - {}
EOF

kubectl apply -f 06-bypass-attempt.yaml
  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. Запустите следующий блок, чтобы создать и выполнить скрипт автоматической проверки. Этот скрипт выполняет необходимые тесты трафика и запрашивает Cloud Logging, используя точную схему метаданных корневого уровня GKE, чтобы отобразить точные правила Dataplane V2, которые оценивали запросы:
cat << 'EOF' > verify-suite.sh
#!/usr/bin/env bash
set -e

echo "========================================================"
echo " 🧪 RUNNING MULTI-TIER POLICY VERIFICATION SUITE"
echo "========================================================"

# Test 1: Ingress Authorized Client -> Gemma (Allowed by NetworkPolicy)
echo -e "\n[1/5] Testing Authorized Client -> Gemma Serving..."
if kubectl exec -n client-ns deploy/rag-agent-client -- \
  curl -s --connect-timeout 3 \
  http://gemma-service.gemma-llm.svc.cluster.local:8000/v1/chat/completions > /dev/null; then
  echo "  ✅ RESULT: FORWARDED (Allowed by Developer Network Policy)"
else
  echo "  🛑 RESULT: FAILED"
fi

# Test 2: Ingress Rogue Client -> Gemma (Blocked by Baseline Tier)
echo -e "\n[2/5] Testing Unauthorized Rogue Tenant -> Gemma Serving..."
if kubectl exec -n untrusted-tenant deploy/rogue-client -- \
  curl -s --connect-timeout 3 \
  http://gemma-service.gemma-llm.svc.cluster.local:8000/v1/chat/completions > /dev/null; then
  echo "  ⚠️ RESULT: UNEXPECTED ALLOW"
else
  echo "  🛑 RESULT: DROPPED (Blocked by Baseline Tier)"
fi

# Test 3: Egress Gemma -> GCP Metadata Server (Blocked by Admin Tier)
echo -e "\n[3/5] Testing Metadata access from Gemma serving pod (gemma-llm)..."
kubectl exec -n gemma-llm deploy/gemma-serving -- \
  curl --connect-timeout 3 -s -w "\nHTTP Code: %{http_code}\n" -H "Metadata-Flavor: Google" \
  http://169.254.169.254/computeMetadata/v1/instance/zone || echo "  🛑 RESULT: Connection Timed Out (Blocked)"

# Test 4: Egress Authorized Client -> GCP Metadata Server (Blocked by Admin Tier)
echo -e "\n[4/5] Testing Metadata access from authorized client (client-ns)..."
kubectl exec -n client-ns deploy/rag-agent-client -- \
  curl --connect-timeout 3 -s -w "\nHTTP Code: %{http_code}\n" -H "Metadata-Flavor: Google" \
  http://169.254.169.254/computeMetadata/v1/instance/zone || echo "  🛑 RESULT: Connection Timed Out (Blocked)"

# Test 5: Egress Rogue Client -> GCP Metadata Server (Blocked by Admin Tier)
echo -e "\n[5/5] Testing Metadata access from rogue client (untrusted-tenant)..."
kubectl exec -n untrusted-tenant deploy/rogue-client -- \
  curl --connect-timeout 3 -s -w "\nHTTP Code: %{http_code}\n" -H "Metadata-Flavor: Google" \
  http://169.254.169.254/computeMetadata/v1/instance/zone || echo "  🛑 RESULT: Connection Timed Out (Blocked)"

echo -e "\n========================================================"
echo " 📋 FETCHING AUDIT TRAIL FROM CLOUD LOGGING"
echo "========================================================"
echo "Waiting 12 seconds for telemetry ingestion..."
sleep 12

echo -e "\n🔍 1. Validating Baseline Tier (Rogue Client Block):"
gcloud logging read '
  resource.type="k8s_node"
  logName=~"logs/policy-action"
  jsonPayload.disposition="deny"
  jsonPayload.src.namespace="untrusted-tenant"
  jsonPayload.dest.namespace="gemma-llm"
' --limit=1 --format="table(
    timestamp.date('%H:%M:%S', tz=LOCAL):label=TIME,
    jsonPayload.src.namespace:label=SRC_NS,
    jsonPayload.dest.namespace:label=DEST_NS,
    jsonPayload.policies[0].name:label=BLOCKED_BY_POLICY
)"

echo -e "\n🔍 2. Validating Admin Tier (Metadata Block across all namespaces):"
gcloud logging read '
  resource.type="k8s_node"
  logName=~"logs/policy-action"
  jsonPayload.disposition="deny"
  jsonPayload.connection.dest_ip="169.254.169.254"
' --limit=3 --format="table(
    timestamp.date('%H:%M:%S', tz=LOCAL):label=TIME,
    jsonPayload.src.namespace:label=SRC_NS,
    jsonPayload.connection.dest_ip:label=DEST_IP,
    jsonPayload.policies[0].name:label=BLOCKED_BY_POLICY
)"
EOF

chmod +x verify-suite.sh
./verify-suite.sh
  1. Пока скрипт выполняется в первой вкладке, следите за выводом Hubble во второй.

На вкладке Hubble (Хаббл) авторизованные HTTP-запросы, которые были ПЕРЕНАПРАВЛЕНЫ, будут выделены зеленым цветом. Для заблокированного трафика вы увидите красные события (DROPPED). The Rogue Tenant. Подключение передается уровнем Admin Tier и блокируется уровнем Baseline Tier. Запрос метаданных. Запрос на получение учетных данных облака немедленно блокируется на уровне администратора.

7d68cc0e9b90f820.png

В Cloud Logging (вкладка 1). Скрипт завершает работу, извлекая точные записи аудита, отправленные в Google Cloud Logging. Это позволяет точно определить, какой уровень (администраторский или базовый) и какое правило заблокировали неавторизованный трафик. Временные метки указываются с учетом часового пояса.

b9e48fd473451ca2.png

10. Организовать

  1. Закрыть вторую вкладку
  2. Переключитесь на первую вкладку Cloud Shell.
  3. Удалите развернутые манифесты, пространства имен и секреты Kubernetes в следующем порядке:
cd ~/gke-cnp-lab

# Delete policy and verification scripts
rm -f verify-suite.sh
kubectl delete -f 05-admin-guardrails.yaml --ignore-not-found
kubectl delete -f 04-app-networkpolicy.yaml --ignore-not-found
kubectl delete -f 03-baseline-deny.yaml --ignore-not-found
kubectl delete -f 00-network-logging.yaml --ignore-not-found

# Delete workload deployments, secrets, and namespaces
kubectl delete -f 02-gemma4-tpu-vllm.yaml --ignore-not-found
kubectl delete secret hf-secret -n gemma-llm --ignore-not-found
kubectl delete -f 01-namespaces-clients.yaml --ignore-not-found
  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. Поздравляю!

Вы успешно завершили практическую работу "Защита рабочих нагрузок инференса ИИ с помощью ClusterNetworkPolicy в GKE".

Выполнив эту практическую работу, вы разработали и проверили надежную многоуровневую сетевую архитектуру углубленной защиты для обслуживания конфиденциальных частных рабочих нагрузок LLM в Google Kubernetes Engine.

Чего вы достигли:

  • Инфраструктура как код. С помощью Terraform динамически развернуты высокозащищенный частный стандартный кластер GKE, частная региональная подсеть и выделенное оборудование Cloud TPU v5e.
  • Телеметрия и наблюдаемость. Настроено ведение сетевого журнала Dataplane V2 и трассировка Hubble eBPF на уровне ядра, чтобы обеспечить глубокую видимость пакетов во время выполнения.
  • Иерархическая структура безопасности. Например, четкое разделение операционных обязанностей между тремя уровнями оценки:
  • Уровень администратора. Принудительно применяемые на уровне кластера, не подлежащие обходу ограничения исходящего трафика, блокирующие доступ к учетным данным метаданных (169.254.169.254).
  • Уровень NetworkPolicy. Делегированные границы входящего трафика в области пространства имен для разработчиков приложений, чтобы безопасно авторизовать подключения клиентов к рабочей версии.
  • Базовый уровень. Создана резервная система безопасности на уровне кластера, которая по умолчанию блокирует неавторизованный трафик арендаторов.
  • Аудит в реальном времени. Запускайте полный набор тестов для проверки, отслеживайте потери пакетов на уровне ядра Linux в реальном времени и запрашивайте структурированные аудиты действий, связанных с правилами, непосредственно в Google Cloud Logging.

Дальнейшие действия и дополнительная информация

Подробнее о сетях GKE…