В предыдущей статье «Beyond Ingress: A Deep Dive into GKE Gateway API, NEGs, and Dataplane V2» я рассказывал, как использовать GKE Gateway для публичного доступа к приложению из кластера GKE в качестве альтернативы Ingress, а также разбирал Dataplane V2 для прямой связи между подами и группы сетевых конечных точек (Network Endpoint Groups, NEGs). В этой статье мы снова обратимся к GKE Gateway, но теперь рассмотрим внутренний региональный шлюз (Internal Regional Gateway) для балансировки трафика «восток–запад» (east-west), то есть трафика между сервисами внутри кластера.
Опираясь на материал предыдущей статьи, мы включим в архитектуру внешний шлюз GKE, а для балансировки межсервисных взаимодействий в приложении Bank of Anthos воспользуемся классом шлюза (gatewayclass) внутреннего регионального балансировщика нагрузки (gke-l7-rilb).
Начнём с настройки безопасности: создадим сертификат для внешнего шлюза и секрет Kubernetes для внутреннего. В основе подхода лежит модель «Нулевого доверия» (Zero-Trust), обеспечивающая сквозную защиту приложения.
Создание внешнего и внутреннего сертификатов
В этой конфигурации мы развернём как внешний, так и внутренний шлюз: первый обеспечит доступ к приложению из интернета, второй — безопасную балансировку трафика между бэкенд-сервисами с помощью внутреннего регионального шлюза.
Сертификат для внешнего шлюза
Внешний шлюз (gke-l7-global-external-managed) смотрит в интернет, поэтому сертификат для HTTPS обязателен. Настроим его.
Включите API Certificate Manager:
gcloud services enable certificatemanager.googleapis.com
Создайте сертификат:
openssl req -x509 -newkey rsa:2048 -nodes -sha256 -days 365 \
-keyout boa-private-key.pem \
-out boa-cert.pem \
-subj "/CN=your.fqdn.com" \
-addext "subjectAltName=DNS:your.fqdn.com"
Загрузите внешний сертификат в Certificate Manager:
gcloud certificate-manager certificates create bank-external-cert \
--certificate-file="your-cert.pem" \
--private-key-file="your-private-key.pem" \
--location="global"
Создайте карту и запись Certificate Manager:
gcloud certificate-manager maps create bank-external-map --location="global"
gcloud certificate-manager maps entries create bank-external-entry \
--map="bank-external-map" \
--hostname="your.fqdn.com" \
--certificates="bank-external-cert" \
--location="global"
На этом настройка внешнего сертификата завершена.
Внутренний региональный шлюз и секрет Kubernetes
Для внутреннего регионального шлюза (gke-l7-rilb) в данной архитектуре будет использоваться секрет Kubernetes (Kubernetes Secret) для внутреннего SSL. Такой подход соответствует рекомендациям по шифрованию межсервисных взаимодействий в GKE, поскольку учётные данные хранятся непосредственно в кластере.
Создайте внутренний сертификат:
openssl req -x509 -newkey rsa:2048 -nodes -sha256 -days 365 \
-keyout your-internal-private-key.pem \
-out your-internal-cert.pem \
-subj "/CN=api.internal.bank" \ #пример внутреннего API, можно использовать это значение
-addext "subjectAltName=DNS:api.internal.bank" #пример внутреннего API, можно использовать это значение
Сначала создайте пространство имён (namespace), в котором будут размещены приложения в кластере GKE. Воспользуемся пространством имён bank-of-anthos. Затем создайте секрет Kubernetes из только что созданного файла внутреннего сертификата в пространстве имён bank-of-anthos:
kubectl create namespace bank-of-anthos
namespace/bank-of-anthos created
Теперь создайте секрет Kubernetes в этом пространстве имён:
kubectl create secret tls boa-internal-cert-secret \
--cert=your-internal-cert.pem \
--key=your-internal-private-key.pem \
--namespace bank-of-anthos
secret/your-internal-cert-secret created
Сеть и подсеть только для прокси
Внутренние региональные шлюзы основаны на Envoy. Google Cloud не использует один IP-адрес на мосту — вместо этого для запуска прокси выделяется пул IP-адресов. Этот пул и есть подсеть только для прокси (Proxy-only Subnet). Настроим её в нашей VPC.
gcloud compute networks subnets create gke-proxy-only-subnet \
--purpose=REGIONAL_MANAGED_PROXY \
--role=ACTIVE \
--region=REGION \
--network=NETWORK \
--range=CIDR
Теперь зарезервируем внутренний IP-адрес для внутреннего регионального шлюза, чтобы адрес внутреннего API Bank of Anthos (api.internal.bank) не изменился в случае удаления и повторного создания шлюза. Укажите имя подсети VPC, в которой работают узлы GKE — не имя подсети только для прокси, созданной на предыдущем шаге, — а в качестве параметра purpose используйте ключевое слово SHARED_LOADBALANCER_VIP:
gcloud compute addresses create bank-internal-lb-ip \
--region=us-west1 \
--subnet=vpc-01-subnet-01 \
--purpose=SHARED_LOADBALANCER_VIP
Развёртывание внешнего и внутреннего шлюзов
Теперь объединим всё вместе: применим глобальный внешний шлюз с созданной картой сертификатов и региональный внутренний шлюз с секретом Kubernetes.
Создайте файл external-gateway.yaml:
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: bank-external-gateway
namespace: bank-of-anthos
annotations:
networking.gke.io/certmap: bank-external-map
spec:
gatewayClassName: gke-l7-global-external-managed
listeners:
- name: https
protocol: HTTPS
port: 443
allowedRoutes:
namespaces:
from: Same
Создайте файл internal-gateway.yaml:
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: bank-internal-gateway
namespace: bank-of-anthos
spec:
gatewayClassName: gke-l7-rilb
listeners:
- name: https
protocol: HTTPS
port: 443
tls:
mode: Terminate
certificateRefs:
- name: boa-internal-cert-secret # Секрет Kubernetes, созданный на предыдущем шаге
allowedRoutes:
namespaces:
from: Same
addresses:
- type: NamedAddress
value: bank-internal-lb-ip
Примените созданные файлы .yaml к кластеру GKE:
kubectl apply -f external-gateway.yaml -n bank-of-anthos
kubectl apply -f internal-gateway.yaml -n bank-of-anthos
Убедитесь, что оба шлюза успешно развёрнуты:
kubectl get gateways -n bank-of-anthos
NAME CLASS ADDRESS PROGRAMMED AGE
bank-external-gateway gke-l7-global-external-managed 34.149.120.99 True 20h
bank-internal-gateway gke-l7-rilb 192.168.15.215 True 20h
Частая точка путаницы — соотношение между IP-адресом внутреннего регионального шлюза и подсетью только для прокси. Удобно представить их как два последовательных этапа единого пути:
Точка входа: виртуальный IP-адрес шлюза (Gateway VIP)
Виртуальный IP-адрес внутреннего регионального шлюза (192.168.15.215) выделяется из основной подсети VPC — той же, где находятся узлы GKE. Этот адрес служит стабильной конечной точкой для сервисов. Когда фронтенд обращается к бэкенд-сервисам, трафик направляется именно на этот VIP.
Подсеть только для прокси
VIP — это адрес назначения, однако подсеть только для прокси (192.168.64.0/20) обеспечивает реальную «рабочую площадку» для балансировщика нагрузки. Google Cloud разворачивает пул прокси Envoy для обработки трафика; этим прокси необходимы собственные выделенные IP-адреса из указанного диапазона для работы внутри VPC.
Передача управления: Source NAT (SNAT)
Когда запрос поступает на Gateway VIP по адресу 192.168.15.215, сетевой уровень передаёт его прокси Envoy. На этом этапе происходит трансляция исходящих адресов (Source Network Address Translation, SNAT).
В результате, когда трафик достигает подов Bank of Anthos, исходный IP-адрес, который видит под, будет адресом из подсети только для прокси, а не исходным IP-адресом клиента или Gateway VIP.
Развёртывание приложения Bank of Anthos
Инфраструктура gatewayclass готова — развернём приложение Bank of Anthos. Клонируйте приложение из публичного репозитория. Это развернёт его в конфигурации по умолчанию; необходимые изменения внесём после того, как все поды запустятся.
git clone https://github.com/GoogleCloudPlatform/bank-of-anthos
cd bank-of-anthos/
Разверните приложение Bank of Anthos в пространстве имён bank-of-anthos:
kubectl apply -f ./extras/jwt/jwt-secret.yaml -n bank-of-anthos
kubectl apply -f ./kubernetes-manifests -n bank-of-anthos
Убедитесь, что все поды находятся в состоянии RUNNING:
kubectl get pods -n bank-of-anthos
NAME READY STATUS RESTARTS AGE
accounts-db-0 1/1 Running 0 19h
balancereader-5db49dfd95-9qz9t 1/1 Running 0 19h
contacts-678bb46775-d7m8p 1/1 Running 0 176m
frontend-777c7b7cf6-z8x48 1/1 Running 0 3h19m
ledger-db-0 1/1 Running 0 19h
ledgerwriter-78f66947b6-xnnms 1/1 Running 0 19h
loadgenerator-5965569b75-9rhlx 1/1 Running 0 3h11m
transactionhistory-7c6f5d8665-cnwnq 1/1 Running 0 176m
userservice-56bdc48976-fhks4 1/1 Running 0 176m
Развёртывание HTTPRoute
Следующий шаг — развернуть маршруты HTTPRoute, которые укажут как внешнему пользовательскому шлюзу, так и внутреннему межсервисному шлюзу, к каким сервисам обращаться.
Внешний маршрут
Сервис frontend должен быть доступен снаружи, поэтому начнём с HTTPRoute для него и привяжем маршрут к внешнему шлюзу.
Создайте файл route-external.yaml:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: frontend-external-route
namespace: bank-of-anthos
spec:
parentRefs:
- name: bank-external-gateway
rules:
- backendRefs:
- name: frontend
port: 80
Внутренний маршрут
Для маршрутизации трафика между frontend и внутренними сервисами через внутренний региональный шлюз определим HTTPRoute с перезаписью пути. Это гарантирует корректную маршрутизацию запросов к /balance и /ledger на корень соответствующих бэкенд-сервисов.
Создайте файл route-internal.yaml:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: backend-internal-route
namespace: bank-of-anthos
spec:
parentRefs:
- name: bank-internal-gateway
hostnames:
- "api.internal.bank"
rules:
- matches:
- path:
type: PathPrefix
value: "/balance"
filters:
- type: URLRewrite
urlRewrite:
path:
type: ReplacePrefixMatch
replacePrefixMatch: "/"
backendRefs:
- name: balancereader
port: 80
- matches:
- path:
type: PathPrefix
value: "/ledger"
filters:
- type: URLRewrite
urlRewrite:
path:
type: ReplacePrefixMatch
replacePrefixMatch: "/"
backendRefs:
- name: ledgerwriter
port: 80
Перенаправление трафика: интеграция фронтенда с внутренним шлюзом
Технические пояснения: зачем эти изменения необходимы
По умолчанию сервисы Bank of Anthos взаимодействуют через стандартный Kubernetes DNS (например, http://balancereader:8080). Описанные ниже изменения «ломают» это поведение по умолчанию, принудительно направляя весь трафик «восток–запад» от frontend через безопасные управляемые прокси Envoy внутреннего шлюза.
В продуктивной среде доверенный корневой центр сертификации (Root CA) обычно встраивается в хранилище доверия пода. В рамках данной лабораторной работы установка NODE_TLS_REJECT_UNAUTHORIZED="0" — стандартный способ обойти проверку TLS и позволить приложению принять самоподписанный сертификат внутреннего шлюза.
Сначала зададим переменные окружения, указывающие на наш HTTPS-эндпоинт. Эта команда перенастраивает приложение frontend для отправки запросов к бэкенду через внутренний шлюз с использованием новых HTTPS-эндпоинтов и пользовательского имени хоста. Кроме того, она отключает проверку TLS-сертификата, чтобы приложение принимало самоподписанные сертификаты, используемые в данной лабораторной среде, без ошибок безопасности:
kubectl set env deployment/frontend -n bank-of-anthos \
BALANCEREADER_API_ADDR="https://api.internal.bank/balance" \
LEDGERWRITER_API_ADDR="https://api.internal.bank/ledger" \
NODE_TLS_REJECT_UNAUTHORIZED="0"
Затем необходимо применить патч к деплойменту frontend. Этот патч заставляет приложение frontend разрешать имя хоста api.internal.bank в VIP-адрес внутреннего шлюза без изменения DNS-зон VPC. Обновляя файл /etc/hosts пода, мы гарантируем корректную маршрутизацию всех внутренних межсервисных вызовов через региональный балансировщик нагрузки для SSL-терминации и перезаписи путей:
kubectl patch deployment frontend -n bank-of-anthos --type='merge' -p '
{
"spec": {
"template": {
"spec": {
"hostAliases": [
{
"ip": "192.168.15.215", #замените на свой IP-адрес
"hostnames": ["api.internal.bank"]
}
]
}
}
}
}'
Проверка: верификация архитектуры «Нулевого доверия»
Инфраструктура развёрнута в соответствии с целевой архитектурой, приложение Bank of Anthos пропатчено — пора убедиться, что трафик проходит через шлюзы так, как ожидается.
Поскольку контейнер frontend использует минималистичный образ, для проверки задействуем эфемерный отладочный контейнер Kubernetes (ephemeral debug container) с подробным тестированием через curl. Это позволит проверить SSL-рукопожатие и маршрутизацию по путям с точки зрения приложения.
Запустите отладочный контейнер, прикреплённый к поду frontend:
kubectl debug -it $(kubectl get pods -l app=frontend -n bank-of-anthos -o jsonpath='{.items[0].metadata.name}') \
-n bank-of-anthos \
--image=curlimages/curl \
--profile=general \
--target=front -- sh
Оказавшись внутри отладочного контейнера — вы увидите приглашение /home/curl_user $ — выполните curl к внутреннему эндпоинту баланса:
curl -kv https://api.internal.bank/balance
В выводе должны присутствовать HTTP/1.1 200 OK и подробная информация о процессе рукопожатия.
Теперь проверим сквозной пользовательский сценарий, открыв внешний шлюз в браузере. Совет: не забудьте обновить записи DNS, иначе разрешение имён не будет работать.
Откройте https://your-fqdn.com.
Войдите с учётными данными по умолчанию (admin/password).
Если отображаются общий баланс и история транзакций, вся цепочка работает корректно:
Пользователь → Внешний шлюз (публичный SSL) → Под frontend → Внутренний шлюз (приватный SSL) → Бэкенд-сервисы.
Заключение
Перейдя от Ingress к GKE Gateway API, мы реализовали надёжную сетевую модель «Нулевого доверия», обеспечивающую безопасность трафика как на периметре, так и внутри кластера. Эта архитектура предоставляет масштабируемый способ управления сложными межсервисными взаимодействиями, сохраняя централизованный контроль над SSL/TLS-терминацией и маршрутизацией трафика.