Основное про API-шлюз в Kubernetes

Существует множество альтернатив для доступа к модулю извне кластера. Шлюз API - это определенно новинка этой области, и потому выбран темой этой статьи.

Ранее мы описывали несколько способов доступа к модулям Kubernetes. Так, например, доступ к модулю pods можно получить через его IP-адрес, но важно учитывать, что поды по своей сути являются временными. Штатный способ - настроить Service: в этом случае IP-адрес стабилен, а задача Kubernetes - обеспечивать мапироание между Service и соответствующими ей подами. В настоящий момент доступны различные виды сервисов: только внутренние, NodePort, позволяющий открыть доступа извне кластера, и LoadBalancer, который полагается на сторонний компонент - обычно это на облачный провайдер. Не будем забывать и об Ingress, обеспечивающем маршрутизацию.

Ну а API-шлюз, как новинку в этой области, мы оставили на десерт, решив посвятить ему целый пост.

От Ingress к API-шлюзу

Внешний доступ к подам Kubernetes прошел несколько эволюционных этапов. Так, например, Ingress - это ответ на проблему отсутствия маршрутизации в LoadBalancer. Но самой большой проблемой Ingress, в свою очередь, является его зависимость от "проприетарных" объектов. В качестве напоминания, приведем в пример фрагмент для создания маршрутизации с использованием Apache APISIX:

apiVersion: apisix.apache.org/v2beta3            #1
kind: ApisixRoute                                #1
metadata:
  name: apisix-route
spec:
  http:
  - name: left
    match:
      paths:
      - "/left"
    backends:
      - serviceName: left
        servicePort: 80
  - name: right
    match:
      paths:
        - "/right"
    backends:
      - serviceName: right
        servicePort: 80

#1: Проприетарные объекты

Проприетарные объекты определенно являются проблемой при осуществлении миграции. Хотя миграция от одного поставщика к другому встречается довольно редко, тем не менее она должна быть максимально плавной. При использовании проприетарных объектов сначала необходимо сопоставить старые объекты с новыми. Существует большая вероятность того, что мапирование "one-to-one" в данном случае может не получиться. Затем важно перевести спецификацию в новую модель, что превращает весь процесс в полноценный сложный проект.

Идея, лежащая в основе Gateway API, заключается в том, чтобы иметь четкое разделение между стандартными объектами и проприетарной реализацией.

API-шлюз

Шлюз API - это проект с открытым исходным кодом, управляемый сообществом SIG-NETWORK. Это набор ресурсов, которые моделируют сервисную сеть в Kubernetes. Такие ресурсы -  GatewayClass, Gateway, HTTPRoute, TCPRoute, Service и т.д. - направлены на развитие сервисной сети Kubernetes с помощью ярких, гибких и клиентоориентированных  интерфейсов, которые уже реализуются и поддерживаются многими крупными компаниями.

- Kubernetes Gateway API Introduction

Исходя из вышеприведенного определения, важно упомянуть об одной из актуальных организационных проблем: разные роли должны управлять разным набором объектов.

Действительно, задачи оператора кластера и девелопера довольно сильно отличаются друг от друга. Это чем-то напоминает старые серверы приложений Java EE, которые предлагали спецификацию, организованную вокруг ролей: девелоперы, deploy-специалисты и операторы. ИМХО, наиболее существенное отличие заключается в том, что спецификация была ориентирована в основном на опыт девелоперов; остальное зависело от исполнителей. Шлюз API, в свою очередь, не оставляет без внимания ни одну сторону.

Настройка доступа к поду через API-шлюз

Давайте заменим упомянутый ранее Ingress на API-шлюз. Для этого необходимо выполнить следующие действия.

Установите новые CRD для шлюза

k apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v0.5.0/standard-install.yaml

Установите Implementation

Я буду использовать Apache APISIX. Список возможных альтернатив представлен на сайте  SIG.

helm install apisix apisix/apisix \
  --namespace ingress-apisix \
  --create-namespace \
  --devel \                                                                #1
  --set gateway.type=NodePort \                                            #2
  --set gateway.http.nodePort=30800 \                                      #2
  --set ingress-controller.enabled=true \                                  #2
  --set ingress-controller.config.kubernetes.enableApiGateway=true \       #3
  --set ingressPublishService="ingress-apisix/apisix-gateway"              #4

#1: Без опции --devel Helm устанавливает последнюю версию, которая не работает с API-шлюза.

#2: Шлюз в любом случае должен быть доступен за пределами кластера.

#3: Здесь происходит волшебство!

#4: Мы вернемся к этому позже.

Проверим, что все работает:

k get all -n ingress-apisix
pod/apisix-5fc9b45c69-cf42m                      1/1     Running   0          14m         #1
pod/apisix-etcd-0                                1/1     Running   0          14m         #2
pod/apisix-etcd-1                                1/1     Running   0          14m         #2
pod/apisix-etcd-2                                1/1     Running   0          14m         #2
pod/apisix-ingress-controller-6f8bd94d9d-wkzfn   1/1     Running   0          14m         #3

NAME                                TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)
service/apisix-admin                ClusterIP   10.96.69.19     <none>        9180/TCP
service/apisix-etcd                 ClusterIP   10.96.226.79    <none>        2379/TCP,2380/TCP
service/apisix-etcd-headless        ClusterIP   None            <none>        2379/TCP,2380/TCP
service/apisix-gateway              NodePort    10.96.101.224   <none>        80:30800/TCP#4
service/apisix-ingress-controller   ClusterIP   10.96.141.230   <none>        80/TCP

#1: Непосредственно сам Apache API SIX

#2: Apache API SIX хранит свою конфигурацию в etcd. На диаграмме по умолчанию запланировано три пода, что вполне практично для разрешения неполадок в распределенных системах.

#3: Контроллер Apache API SIX: Контроллер Kubernetes - это цикл управления, который преобразовывает существующее состояние в желаемое.

#4: Служба Apache APISIX: Это служба NodePortService, которую мы установили через Helm Chart. Это также имя, на которое мы ссылались во время установки ingressPublishService в Helm Chart. На данном этапе вся инфраструктура готова.

Реализация

Как уже упоминалось выше, API четко разделяет спецификацию и реализацию. Однако их необходимо как-то связать. Это ответственность объекта GatewayClass:

apiVersion: gateway.networking.k8s.io/v1alpha2          #1
kind: GatewayClass                                      #2
metadata:
  name: apisix-gateway-class                            #3
spec:
  controllerName: apisix.apache.org/gateway-controller  #4

#1: Мы не используем последнюю версию специально, так как Apache API SIX работает с этой версией. Но даже не сомневайтесь, что в самом ближайшем будущем все обновится.

#2: Объект GatewayClass 

#3: Назовите его так, как вы хотите; однако позже мы будем использовать его для ссылки на класс шлюза.

#4: Имя контроллера зависит от реализации. В данном случае мы используем Apache APISIX.

Обратите внимание, что GatewayClass  имеет область действия в масштабе всего кластера. Эта модель позволяет нам объявлять различные реализации API-шлюза и использовать их параллельно внутри одного и того же кластера.

Создание шлюза

Благодаря Apache API SIX здесь все довольно просто:

apiVersion: gateway.networking.k8s.io/v1alpha2          #1
kind: Gateway                                           #2
metadata:
  name: apisix-gateway
spec:
  gatewayClassName: apisix-gateway-class                #3
  listeners:                                            #4
    - name: http
      protocol: HTTP
      port: 80

#1: Тот же namespace, что и выше

#2: Объект Gateway 

#3: Ссылка на класс шлюза, обозначенная ранее

# 4: Разрешите некоторые ограничения на этом уровне, чтобы оператор кластера имел возможность избежать нежелательного использования

Предупреждение: API-шлюз предоставляет возможность динамического изменения порта на стороне оператора. На момент написания этой статьи распределение портов Apache APISIX является статическим. Очевидно, что динамическим он станет уже в ближайшем будущем - советуем следить за выпусками GitHub, чтобы быть в курсе изменений.

Маршрутизация, маршрутизация и еще раз маршрутизация

До этого момента речь велась об инфраструктуре, и наконец-то мы добрались до маршрутизации.

Я хочу поработать с тем же маршрутом, что и в предыдущем посте: бранчи  /left и right. Опустим описание последнего для краткости.

apiVersion: gateway.networking.k8s.io/v1alpha2          #1
kind: HTTPRoute                                         #2
metadata:
  name: left
spec:
  parentRefs:
    - name: apisix-gateway                              #3
  rules:
    - matches:                                          #4
      - path:                                           #4
          type: PathPrefix                              #4
          value: /left
      backendRefs:                                      #5
        - name: left                                    #5
          port: 80                                      #5
  1. То же пространство имен, что и выше

  2. Объект HTTPRoute 

  3. Ссылка на Gateway, созданный выше

  4. Совпадение правил (rules). В нашем случае мы сопоставляем префикс пути, но возможны разные вариации правил. Вы можете сопоставлять их на базе параметра запроса, заголовка и т.д.

  5. "Вышестоящий" сервер для переадресации. В предыдущем посте мы как раз давали определение leftService.

Проверяем в работе

Отстроив процессы маршрутизации, самое время проверить как все работает.

curl localhost:30800/left

Во время установки Helm chart, мы дали команду Apache APISIX создать службу NodePort на порту 30800. Таким образом, мы можем использовать порт для доступа к сервису за пределами кластера.

left

Подведем итоги

Существует множество вариантов для доступа к модулю извне кластера. CNCF осветили большую часть актуальных на данный момент альтернатив.

API-шлюз - это новинка в индустрии. Спецификация все еще находится на стадии разработки, а продукты - на разных этапах внедрения. По этой причине еще слишком рано переносить все свое производство на API. Тем не менее, мы настоятельно рекомендуем следить за процессами обновления и модернизации, ибо мы убедились в списке очевидных преимуществ по сравнению с предыдущими подходами.

Полный исходный код этого поста можно найти на GitHub.