Предыдущие статьи:
Kubernetes просто, часть 5: зачем нужен Ingress и как маршрутизировать HTTP-трафик
Kubernetes просто, часть 1: зачем Kubernetes, устройство кластера
Лабораторные работы:
Kubernetes просто лабы, часть 1: поднимаем кластер и знакомимся с его устройством
В прошлой части мы успели разобрать, что такое Ingress и в итоге пришли к схеме:
Internet - Ingress Controller - Service - Pods
Ingress позволил решить важную задачу - вместо отдельной внешней точки доступа для каждого приложения он представил нам возможность сделать общую точку входа для маршрутизации HTTP-трафика:

Всё это довольно разумно и, должно быть, когда я скажу, что тема, которую мы будем разбирать сегодня предназначена для выполнения практически той же задачи, у вас возникнет закономерный вопрос: «Если Ingress это уже умеет, зачем нужен еще и Gateway API?».
Давайте разбираться!
Что не так с Ingress?
Отвечу сразу - с ним всё в порядке.
Он отлично решает задачу, которую мы разбирали в прошлой части.
Есть несколько приложений:
person.marketplace.com api.marketplace.com
Есть 2 Service:
person-sercice api-service
Мы хотим получить:
person.marketplace.com -> person-sercice api.marketplace.com -> api-service
Создаём Ingress, описываем правила маршрутизации - получаем нужный результат.
Поэтому проблема, решаемая Gateway API не в том, что Ingress работает плохо - проблема в возможности масштабирования такой инфраструктуры. Сейчас объясню.
Представьте, наш мини-маркетплейс вырос в настоящий взрослый проект - большой конкурент серьезных компаний.
Теперь там работают уже не 2 домена, а, скажем 4:
shop.marketpalce.com api.marketplace.com admin.marketplace.com person.marketplace.com
Но, что важнее, за ними стоят разные команды. Команда фронтендеров отвечает за shop.marketplace.com, команда бэкендеров за api.marketplace.com, администраторы за admin.marketplace.com и так далее.
При этом существует еще одна команда, скажем Команда Развертывания - она как раз занимается контролем общей точки входа.
Internet -> общая точка входа
К примеру, какие порты доступны снаружи и где настраивается HTTPS.
Разработчикам из других команд это совершенно не интересно, команде фронтендеров интересно shop.marketplace.com -> shop-service, остальным их домены и сервисы, соответственно.
Иначе говоря, здесь появляются две разные задачи -
«Как трафик попадает в кластер и куда отправлять трафик после входа»
Это разделение очень важно для понимания Gateway API.

Разделим две задачи
Пока забудем про названия объектов Kubernetes и сконцентрируемся на понимании желаемой архитектуры.
Нужна точка входа:
Internet -> ТОЧКА ВХОДА
Она принимает весь внешний трафик.
Но после неё нужно решить: "Куда отправлять конкретный запрос?"
Поэтому добавим ещё уровень:
Internet -> ТОЧКА ВХОДА --МАРШРУТ--> Service
Теперь у нас две отдельные сущности:
Первая отвечает за входящий трафик, вторая за правила его маршрутизации.
Именно вокруг такого разделения построена модель Gateway API.
Gateway и HTTPRoute
Теперь можно дать двум сущностям имена - точку входа зовут Gateway, а маршруты называют HTTPRoute.
Теперь всё выглядит как:
Internet - Gateway - HTTPRoute - Service - Pods
Это, на данный момент, главная схема, которую стоит запомнить. Разберем два новых компонента по-отдельности.
Gateway
Начнем с Gateway.
Достойная аналогия - вход в большое офисное здание.
Здесь нас интересует:
Какой трафик принимать?
На каком порту?
По какому протоколу?
К примеру
Internet -> HTTP:80 Gateway
В Gateway API такую задачу описывает kind: Gateway
Упрощённо:
kind: Gateway metadata: name: web-gateway spec: gatewayClassName: example listeners: - name: http protocol: HTTP port: 80
Пока большую часть можно даже не запоминать.
Взгляните на нижнюю часть:
listeners: - name: http protocol: HTTP port: 80
Мы описываем listener (слушателя) и говорим - слушай порт 80, принимай по нему HTTP-трафик.
Пока Gateway вообще не обязан знать, что у нас есть shop-service или api-service, как мы это делали раньше в Ingress.
Мы уже обсуждали, его задача другая - принимать весь трафик. Вот мы и организовали такой вход.

Что делает HTTPRoute?
Теперь пользователь отправляет запрос на http://shop.marketplace.com
Gateway его принял, но куда отправлять его дальше?
Вот тут-то и появляется kind: HTTPRoute.Он описывает правила HTTP-маршрутизации.
К примеру:
shop.marketplace.com -> shop-service
Создадим простой HTTPRoute:
apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: shop-route spec: parentRefs: - name: web-gateway hostnames: - shop.marketplace.com rules: - backendRefs: - name: shop-service port: 80
Выглядит объемно, разберем по частям.
Сначала:
parentRefs: - name: web-gateway
Упрощенно это значит - связан с web-gateway (имя нашей точки входа)
Дальше:
hostnames: - shop.marketplace.com
То есть этот маршрут относится к запросам на shop.marketplace.com
И наконец:
- backendRefs: - name: shop-service port: 80
Мы выбираем куда отправлять входящие запрос. Соберем всё вместе:

Расширяемся
Теперь хотим добавить api.marketplace.com
Для него уже существует api-service, и, вы уже чувствуете, насколько удобен здесь Gateway API.
Объект Gateway у нас уже есть, создавать второй экземпляр не обязательно, точка входа может остаться прежней. Достаточно создать один новый api-route. Получаем:

Разделение ответственности оказалось действительно удобным!
Чем это лучше Ingress?
Теперь можно вернуться к вопросу из начала статьи:
Ingress тоже позволял написать, какие маршруты на какой сервис ссылаются. Зачем тогда это было.
Ключевая идея в модели управления Gateway добавил нам новые объекты, позволяющие модульно делить выполнение работы между командами разработчиков.
Такая модульность как раз намного ближе к тому, как устроен большой Kubernetes-кластер.
А как же контроллер?
Мы уже сталкивались с похожей ситуацией в прошлой части. Сам объект Ingress содержал правила, но для реальной обработки трафика нужен был Ingress Controller.
С Gateway API идея похожая. Gateway и HTTPRoute - это объекты Kubernetes. Они описывают, какую точку входа и какие маршруты мы хотим получить, но сами по себе сетевой трафик не принимают.
В кластере должен быть установлен сетевой компонент, который умеет работать с Gateway API. Он следит за такими объектами и настраивает реальную обработку трафика.
Упрощенно:
Gateway + HTTPRoute ↓ сетевой компонент с поддержкой Gateway API ↓ реальная обработка трафика
То есть мы снова приходим к уже знакомой модели: Kubernetes хранит желаемое состояние, а соответствующий контроллер приводит реальную систему к нему.
А что тогда такое GatewayClass?
Вернемся к манифесту Gateway. В нем у нас была строка:
gatewayClassName: example
Значение example здесь появилось не случайно. Gateway должен понимать, какой установленный в кластере сетевой компонент будет его обслуживать.
Для этой связи и существует отдельный объект - GatewayClass.
сетевой компонент ↓ GatewayClass ↓ Gateway ↓ HTTPRoute
На нашем текущем уровне этого понимания достаточно: GatewayClass связывает Gateway с конкретной реализацией Gateway API, которая умеет превратить наши YAML-объекты в работающую сетевую конфигурацию.
Поэтому строку:
gatewayClassName: example
можно читать примерно так: «для этого Gateway используй GatewayClass с именем example».
Сам GatewayClass обычно появляется в кластере вместе с установленной реализацией Gateway API или создается при ее настройке. В практической части мы посмотрим это руками, поэтому сейчас глубже в контроллеры и конкретные реализации уходить не будем.
Что происходит с Service?
Здесь ничего принципиально не меняется. HTTPRoute направляет запрос не в конкретный Pod, а в Service.
Gateway ↓ HTTPRoute ↓ shop-service ↓ Pod A Pod B Pod C
Если Pod B исчезнет, ReplicaSet создаст новый Pod. У него может быть другой IP и он может оказаться на другой Node, но HTTPRoute менять не придется.
Он по-прежнему указывает на shop-service, а Service уже работает с актуальным набором backend endpoints.
Получается знакомая нам цепочка:
Gateway ↓ HTTPRoute ↓ стабильный Service ↓ динамические Pods
Собираем картину целиком
Теперь можно собрать все объекты, которые появились в этой части, в одну схему:
Internet ↓ Gateway ↓ HTTPRoute ↓ Service ↓ Pods
Gateway отвечает за точку входа. HTTPRoute описывает правило маршрутизации. Service предоставляет стабильный доступ к backend Pods.
А GatewayClass нужен уровнем выше - он связывает Gateway с той реализацией Gateway API, которая действительно умеет обслуживать такую конфигурацию.
Поэтому полную картину можно представить так:
GatewayClass ↓ Gateway ↓ HTTPRoute ↓ Service ↓ Pods
Это прежде всего схема ответственности объектов, которую нам важно закрепить.
Если эта схема понятна, детали YAML и конкретных реализаций дальше будут восприниматься намного проще.
Проверьте себя
1. Если Ingress уже умеет маршрутизировать HTTP-трафик, зачем может понадобиться Gateway API?
2. За что в нашей модели отвечает Gateway?
3. За что отвечает HTTPRoute?
4. Можно ли подключить несколько HTTPRoute к одному Gateway?
5. Почему разделение Gateway и HTTPRoute удобно, если инфраструктурой и приложениями управляют разные команды?
6. Сам объект Gateway принимает сетевые пакеты или только описывает желаемую точку входа?
7. Зачем нужен GatewayClass?