Kubernetes NodePort / LoadBalancer / Ingress? Когда что использовать?

Чем отличаются всякие там NodePorts, LoadBalancer и Ingress? Все они дают возможность внешнему трафику попасть в ваш кластер, но дают эту возможность по-разному. Давайте-ка разберёмся, как они это делают и когда какой тип сервиса лучше использовать.

ClusterIP

ClusterIP — это дефолтный тип сервиса в кубах, он поднимает вам сервис внутри кластера на внутрекластеровом IP. Доступа для внешнего трафика нет, только внутри кластера.

YAML для ClusterIP выглядит как-то так:

apiVersion: v1
kind: Service
metadata:  
  name: my-internal-service
spec:
  selector:    
    app: my-app
  type: ClusterIP
  ports:  
  - name: http
    port: 80
    targetPort: 80
    protocol: TCP

“Так, ну и зачем же рассказывать нам о ClusterIP, если он не принимает внешний трафик?” — спросите вы. А всё потому, что попасть на этот сервис можно через Kubernetes proxy!

Делается это так:

$ kubectl proxy --port=8080

*Прим. пер.: Надо отметить, что эта команда выполняется со своего локального компьютера, где настроен kubectl и для него прописаны доступы в кластер. Таким образом кубовый api-сервер будет доступен через прокси на localhost

Теперь можно попасть на сервис по такому адресу:

http://localhost:8080/api/v1/proxy/namespaces/<NAMESPACE>/services/<SERVICE-NAME>:<PORT-NAME>/

К примеру, чтобы попасть на сервис, описанный выше, надо использовать следующий адрес:

http://localhost:8080/api/v1/proxy/namespaces/default/services/my-internal-service:http/

Когда использовать ClusterIP для открытия внешнего доступа?

Есть парочка вариантов применения, когда стоит пускать трафик на сервис через kubernetes proxy:

  1. Когда надо дебажить сервисы, подключаясь к ним напрямую
  2. Когда надо получить доступ к какой-

Пожалуй, не стоит использовать этот подход, чтобы открывать доступ к сервисам извне, особенно на проде.

*Прим. пер.: Да и кто вообще подумает о том, чтобы открывать так доступы на прод?

NodePort

NodePort — наиболее простой и “тупой” способ пустить внешний трафик на сервис. Название “NodePort” говорит само за себя — просто открывается порт на ноде для доступа извне, а трафик уже маршрутизируется на сам сервис.

Пример YAML для NodePort

apiVersion: v1
kind: Service
metadata:  
  name: my-nodeport-service
spec:
  selector:    
    app: my-app
  type: NodePort
  ports:  
  - name: http
    port: 80
    targetPort: 80
    nodePort: 30036
    protocol: TCP

Как можно было заметить, в описании фигурирует параметр nodePort, который задаёт номер порта для открытия. Если его не задавать, то выберется рандомный порт в диапазоне 30000-32767.

Когда использовать NodePort?

Для начала, у этого метода сразу несколько минусов:

  1. Один порт — один сервис
  2. Диапазон 30000-32767 (хоть и изменяемый, но делать это надо крайне осторожно)
  3. Если IP ноды может измениться (например, в GKE или AWS при автоскейлинге), то это тоже надо учитывать

В общем, не рекомендую использовать этот метод в продакшне. Конечно, если это какой-то демо-сервис и от него не требуется 100% доступность, вполне можно использовать NodePort.

LoadBalancer

LoadBalancer — это, можно сказать, стандартный и самый простой способ пустить внешний трафик на сервис. Правда, работает он только в облаках, т.к. использует нативные облачные решения текущего провайдера. Например, при использовании в GKE будет поднят Network Load Balancer с отдельным внешним IP-адресом, который будет маршрутизировать весь трафик на указанный сервис.

Когда использовать LoadBalancer?

Если надо дать прямой доступ к сервису извне, это ваш выбор. Весь трафик на выбранный порт будет маршрутизироваться на сервис. Это значит, что можно пускать практически любой вид трафика: HTTP, TCP, UDP, Websockets, gRPC и т.п.

Огромный минус в том, что для каждого сервиса будет использоваться свой LoadBalancer со своим внешним IP, и придётся в результате платить сразу за несколько LoadBalancer’ов.

Ingress

В отличие от других приведённых выше примеров Ingress — вообще не сервис как таковой. Его нельзя задать как type в описании сервиса. На деле Ingress поднимается между сервисами и внешним миром и выступает в роли некоего умного маршрутизатора, который является точкой входа в кластер.

С Ingress’ом можно делать очень много разных штук, т.к. существует множество Ingress Controller’ов с разными возможностями. Например, в GKE дефолтный Ingress Controller поднимет HTTP(S) Load Balancer. Он позволяет роутить трафик на сервисы по путям URL’ов и поддоменам. Например, можно отправить всё, что приходит на foo.yourdomain.com на сервис foo, а всё, что приходит на yourdomain.com/bar/ на сервис bar

Примера YAML Ingress для кластера в GKE:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: my-ingress
spec:
  backend:
    serviceName: other
    servicePort: 8080
  rules:
  - host: foo.mydomain.com
    http:
      paths:
      - backend:
          serviceName: foo
          servicePort: 8080
  - host: mydomain.com
    http:
      paths:
      - path: /bar/*
        backend:
          serviceName: bar
          servicePort: 8080

И когда же использовать Ingress?

Пожалуй, Ingress — наиболее мощное средство для того, что пускать внешний трафик на приложения, но при этом сложнее других способов. Есть множество разных Ingress Controller’ов  Google Cloud Load BalancerNginxContourIstio и другие. Для этих контроллеров ещё существуют и плагины, типа  cert-manager, которые могут автоматом выпускать сертификаты для сервисов. Ingress больше всего пригодится, когда надо открыть доступ сразу к пачке сервисов на одном IP и все эти сервисы используют один и тот же L7 протокол (обычно, HTTP). В таком случае придётся платить всего за один LoadBalancer (в случае с gke), а использование дополнительных плюшек в виде всяких SSL, авторизации, роутинга и пр. — очень хорошее подспорье.

Оригинал — https://medium.com/google-cloud/kubernetes-nodeport-vs-loadbalancer-vs-ingress-when-should-i-use-what-922f010849e0

*GKE — Google Kubernetes Engine