в kubernetes

Знакомство с Kubernetes. Часть 11: Сервисы (services)


Поды в кластере Kubernetes смертны — они создаются (рождаются), но когда под по какой-либо причине умирает, то он не воскресает. И хотя каждый под при создании получает свой собственный IP-адрес, этот адрес нельзя назвать постоянным и стабильным вследствие «смертности» подов. Давайте разберемся!

К примеру, ReplicaSet может динамически изменять количество подов в кластере (путем масштабирования), при этом новые поды могут быть запущены на других узлах (нодах) кластера — в этом случае IP-адрес пода наверняка изменится.

Это порождает проблему: если некий набор подов (назовем их backends) предоставляют функционал другому набору подов (назовем их frontends) внутри кластера Kubernetes, то как frontend-поды узнают адреса backend-подов и взаимодействуют с ними?

Сервис в Kubernetes — это абстракция, определяющая логический набор подов и политику доступа к ним (иногда такой набор подов еще называют микросервисом). Как правило, этот набор подов определяется на основе меток (присваиваются в момент создания подов) и селекторов.

Например, backend — это 3 реплики подов, занимающихся обработкой изображений. Все три пода абсолютно идентичны с функциональной точки зрения (так как это реплики), поэтому frontend не должен беспокоиться на какой именно backend-под попадет запрос — это работа сервиса.

Простейший пример сервиса может выглядеть так:

kind: Service
apiVersion: v1
metadata:
  name: my-service
spec:
  selector:
    app: MyApp
  ports:
  - protocol: TCP
    port: 80
    targetPort: 9376

Согласно данной спецификации, будет создан объект «сервис» (service) с именем my-service, перенаправляющий запросы на порт 9376 каждого пода, у которого присутствует метка app=MyApp. Этому сервису также будет назначен отдельный IP-адрес.

Согласно указанному селектору, в кластере непрерывно будут проверяться поды на наличие метки app=MyApp, а результат такой проверки будет публиковаться (с помощью POST) в объект Endpoints с таким же именем — my-service.

Стоит отметить, что сервис может сопоставлять входящий порт (port:) с любым портом назначения (targetPort:). Если параметр targetPort не указан, то по умолчанию будет использоваться тот же порт, что и в параметре port. Еще одна интересная особенность — targetPort может содержать строку с именем порта в подах (а сам номер порта, присвоенный этому имени, может быть разным для каждого пода).

Сервисы чаще всего используются для доступа к подам в кластере Kubernetes, но их также можно использовать и для доступа к другим типам бэкендов, например:

  • внешний кластер баз данных в production-окружении, локальная БД для тестового окружения;
  • совершенно иной сервис в другом неймспейсе или кластере;
  • часть бекендов, запущенных вне кластера Kubernetes (например, если вы только переносите свою инфраструктуру).

В любом из предложенных вариантов следует создать сервис без селекторов, например:

kind: Service
apiVersion: v1
metadata:
  name: my-service
spec:
  ports:
  - protocol: TCP
    port: 80
    targetPort: 9376

и, так как селекторов нет, соотвествующий объект Endpoints не создастся автоматически. Его необходимо создать вручную, тем самым указав связь между сервисом и конечной точкой (бекендом), например так:

kind: Endpoints
apiVersion: v1
metadata:
  name: my-service
subsets:
  - addresses:
      - ip: 1.2.3.4
    ports:
      - port: 9376

Примечание. IP-адрес в данном случае не может находиться в диапазонах 127.0.0.0/8, 169.254.0.0/16 и 224.0.0.0/24.

Для многих сервисов обычным делом является использование (открытие) нескольких портов. В таком случае, все что необходимо — присвоить имена всем портам, например:

kind: Service
apiVersion: v1
metadata:
  name: my-service
spec:
  selector:
    app: MyApp
  ports:
  - name: http
    protocol: TCP
    port: 80
    targetPort: 9376
  - name: https
    protocol: TCP
    port: 443
    targetPort: 9377

В Kubernetes существует несколько вариантов предоставления доступа к сервисам, которые называются типы (ServiceTypes):

  • ClusterIP: предоставляет доступ к сервису на внутреннем IP-адресе кластера (сервис доступен только внутри кластера). Тип ClusterIP используется по умолчанию;
  • NodePort: предоставляет доступ к сервису на IP-адресе каждого узла (ноды) кластера, на статическом порту (из диапазона 30000-32767). Автоматически создастся и сервис типа ClusterIP, на который будут маршрутизироваться запросы с NodePort. Взаимодействовать с сервисом можно также из-за пределов кластера, используя в качестве адреса <NodeIP>:<NodePort>;
  • LoadBalancer: предоставляет доступ к сервису используя балансировщик (load balancer) облачного провайдера. При этом автоматически создаются сервисы типа NodePort и ClusterIP, на которые будут маршрутизироваться запросы с балансировщика;
  • ExternalName: особый случай — сопоставляет имя сервиса с содержимым поля externalName (например, foo.bar.example.com), возвращая CNAME запись. Никакого проксирования не происходит.

Больше информации о сервисах можно найти в официальной документации или в статье Understanding kubernetes networking: services.

Добавить комментарий