Найти в Дзене
bashninja | DevOps & SRE ⚙️

Service

У нас есть несколько инстансов приложения — подов, запущенных в кластере на разных нодах. Очень бы хотелось обращаться не напрямую к каждому поду по их IP адресу, а к некоторому “виртуальному” IP. И при обращении на этот IP-запрос распределялся на один из подов. Это бы существенным образом бы упростило межсервисное взаимодействие. Например, если у нас есть бекенд сервис, который производит обработку изображений, запущенный в трех экземплярах. И есть другой сервис, который использует этот бекенд. Ему все-равно, какой из подов обработает его запрос и сколько всего таких экземпляров запущено. Ему бы гораздо удобнее было бы иметь дело с одним виртуальным IP, при обращении на который запрос распределялся на одну из под. Service – это объект Kubernetes, который описывает некоторый набор подов в качестве сетевого сервиса, а также способ доступа к этому сетевому сервису. В самом простом случае создается некоторый "виртуальный" IP, при обращении на который трафик балансируется по соответствующи
Оглавление

У нас есть несколько инстансов приложения — подов, запущенных в кластере на разных нодах. Очень бы хотелось обращаться не напрямую к каждому поду по их IP адресу, а к некоторому “виртуальному” IP. И при обращении на этот IP-запрос распределялся на один из подов. Это бы существенным образом бы упростило межсервисное взаимодействие.

Например, если у нас есть бекенд сервис, который производит обработку изображений, запущенный в трех экземплярах. И есть другой сервис, который использует этот бекенд. Ему все-равно, какой из подов обработает его запрос и сколько всего таких экземпляров запущено. Ему бы гораздо удобнее было бы иметь дело с одним виртуальным IP, при обращении на который запрос распределялся на одну из под.

Service – это объект Kubernetes, который описывает некоторый набор подов в качестве сетевого сервиса, а также способ доступа к этому сетевому сервису.

В самом простом случае создается некоторый "виртуальный" IP, при обращении на который трафик балансируется по соответствующим подам.

Реализуется это с помощью с встроенного контроллера в Kubernetes и компонента kube-proxy, который производит изменения в правилах маршрутизации трафика на нодах кластера.

Давайте посмотрим, как выглядит описание объекта типа Service.
Давайте посмотрим, как выглядит описание объекта типа Service.

Как и у любого объекта Kubernetes, у него есть ряд общих для всех полей, это apiVersion, kind, metadata, spec.

В spec находится спецификация сервиса:

· selector — определяет, какой набор под попадет под управление сервиса

· ports.port — порт сервиса, запросы на который будут проксироваться на ports.targetPort

· ports.targetPort — порт пода, на котором отвечает приложение

· type — типа сервиса. Определяет каким образом можно получить доступ к сервису:

o ClusterIP — по внутреннему IP в кластере

o NodePort — по порту на ноде кластера

o LoadBalancer — по какому-то внешнему, по отношению к кластеру, IP.

-3

Встроенный в Kubernetes контроллер слушает события о создании, удалении и обновлении объектов типа Service. Как только создается новый объект Service, контроллер находит свободный "виртуальный" IP адрес из подсети и назначает его Service-у. Также события обновления, создания и удаления объектов типа Service слушает kube-proxy на каждой ноде. Как только объекту Service назначен внутренний IP адрес (он также называется ClusterIP), kube-proxy прописывает правила в сетевой файервол (iptables), которые все обращения на CluserIP сервиса маршрутизируют на конкретные IP адреса подов. Поды для балансировки выбираются с помощью селектора, указанного в спецификации. В реализации по умолчанию балансировка равномерная, т.е. все поды будут получать примерно одинаковое количество запросов. Поскольку kube-proxy запущен на всех нодах, то изменения в правилах буду применены на всех нодах кластерах.

Service, в отличие от Pod, является объектом, описывающим сетевую конфигурацию, а не рабочую нагрузку. При создании объекта типа Service никаких новых процессов или балансировщика на рабочих нодах не запускается. Лишь меняются правила маршрутизации трафика на ноде. Поэтому нельзя сказать, что сервис запущен на какой-то из нод. Но иногда говорят, что "сервис балансирует нагрузку на такие-то поды" или "сервис убрал поду из балансировки", или "внутренний ip сервиса". Это является общепринятым упрощением в терминологии.

В случае, если добавляется еще один под, подходящий под селектор, то kube proxy обновляет правила.

И проксирование теперь будет происходить на 3 поды. Опять обновления маршрутизации будут происходить на всех нодах кластера.

ClusterIP

Тип ClusterIP дает возможность обращаться к набору подов, как сетевому сервису, только внутри кластера Kubernetes. Так же как и система обнаружения сервисов Kubernetes, которая работает только при межсервисном взаимодействии, внутри кластера Kubernetes. В случае, если мы хотим получить доступ к сервису извне кластера, мы можем использовать другие типы сервисов.

NodePort

-4

Сервис NodePort — самый простой способ направить внешний трафик в сервис.

Тип сервиса NodePort предоставляет не только внутренний IP внутри кластера, но и закрепляет за конкретным сервисом определенный порт на всех нодах. Обычно выбирается свободный порт из диапазона 30000-32000, но диапазон может отличаться для разных реализаций сетевого плагина в Kubernetes. Можно самому указать порт, но рекомендуют дать Kubernetes выбрать самому.

-5

Механизм обновления такой же как у ClusterIP. Встроенный контроллер и kube-proxy следят за изменениями сущностей типа Service и обновляют правила маршрутизации при необходимости.

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

Такой тип публикации сервиса наружу чаще всего используется в небольших инсталляциях Kubernetes, поскольку для добавления и удаления новых нод необходимо править конфигурацию внешнего балансировщика. Также при изменении IP нод такая схема может приводить к проблемам.

В целом, такую схему работы с NodePort в критичных продакшн кластерах среднего и большого размера не рекомендуется использовать.

LoadBalancer

-6

Есть еще один тип сервиса, который открывает сервис вовне — это LoadBalancer. Помимо порта на нодах и внутреннего IP, также назначается внешний IP адрес, по которому будет доступен сервис. Для работы этого типа сервисов требуется наличие специального, обычно облачного, контроллера, который реализует логику выбора и назначения IP. Если мы создадим сервис с типом LoadBalancer, в инсталляции Kubernetes без соответствующего контроллера, то внешний ip у сервиса не будет назначен. Как конкретно назначается IP, из каких подсетей, и какие конкретно используются для этого правила маршрутизации, зависит от реализации контроллера конкретного облачного провайдера.

Помимо LoadBalancer-а и NodePort-a, для того, чтобы внешний клиентский трафик попадал в кластер, можно использовать сущности Ingress и GatewayAPI, про них мы поговорим в следующем разделе.

Обнаружение сервисов

Т.к. внутренний ClusterIP обычно назначается случайным образом системой, а не устанавливается заранее, то необходим механизм, подсказывающий виртуальный IP сервиса.

В Kubernetes есть два механизма обнаружения сервисов. Первый основан на переменных окружения и мало кем используется, а вот второй работает с помощью системы доменных имен (DNS) и является де-факто стандартом.

По умолчанию в кластере Kubernetes нет компонента, который бы отвечал за DNS, но большинство дистрибутивов Kubernetes предоставляют реализацию DNS сервиса, как правило, на основе coredns, либо его можно поставить самостоятельно. В этом случае при создании сущности Service создается запись в DNS и обеспечивается механизм разрешения доменных имен из подов внутри кластера Kubernetes.

Из подов, которые находятся в том же самом пространстве имен, к сервису можно обратиться просто по его имени - {service-name}. Доменное имя {service-name} внутри пода будет резолвится в IP сервиса. Если под находится в другом namespace, то тогда можно обратиться по доменному имени {service-name}.{namespace}.

Например, если у нас есть сервис, который называется hello-service в пространстве имен myapp, то из подов в том же самом namespace-е можно обращаться по доменному имени hello-service. Из подов, которые находятся в другом неймспейсе, можно обратиться по имени hello-service.myapp. Также можно использовать полноценное доменное имя - hello-service.hello-ns.svc.cluster.local

-7

Ссылки:

· Service (англ)

· DNS в Kubernetes (англ)

#Kubernetes #devops #service #dns