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

Балансировка нагрузки в Kubernetes

Роман Андреев
Роман Андреев
Технический писатель
14 марта 2023 г.
2184
9 минут чтения
Средний рейтинг статьи: 5

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

Главный плюс балансировки состоит в том, что она позволяет избежать простоя приложения. Таким образом можно предотвратить запланированный простой из-за развертывания новой версии ПО или незапланированный из-за проблем с оборудованием. Давайте рассмотрим, как балансирование нагрузки помогает стабилизировать работу кластера Kube, повышая доступность приложений. 

Балансировать нагрузку помогают службы Kubernetes, поэтому дадим их общее описание и покажем, как они работают, а затем приведем конкретные примеры балансировки нагрузки. Но прежде всего о том, как в Kube реализовано отслеживание подов, без которого и сама балансировка была бы крайне затруднительна.

Отслеживание подов в Kubernetes с селектором и без него

Поды в Kubernetes являются временными объектами и получают новый IP при каждом запуске: после выполнения определенной задачи они уничтожаются и затем создаются заново при новом развертывании. При отсутствии инструментов Kubernetes service нам пришлось бы отслеживать IP всех активных подов. И это было бы крайне сложной задачей, особенно по мере того, как наше приложение масштабируется. Служба Kube решает эту проблему благодаря селектору. Посмотрим на такой код (замените значения в ряде строк на нужные):

kind: Service
metadata:
  name: timewebapp
spec:
  selector:
    app: timewebapp
  ports:
    - protocol: TCP
      name: timewebapp
      port: 5428
    targetPort: 5428

Селектор обеспечивает корректность сопоставления служб с относящимися к ним подами. Когда служба получает под с matching label, то происходит обновление IP пода в списках объектов Endpoints. Endpoints отслеживают IP-адреса всех подов и обновляются автоматически, а каждая служба создает свой объект Endpoint.

Впрочем, для наших целей нам не нужно вдаваться в подробности об Endpoints. Просто помните, что именно Endpoints обновляют список IP-адресов, чтобы служба Kube могла перенаправлять свой трафик.

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

kind: Service
  metadata:
    name: timewebapp-without-ep
  spec:
    ports:
      - protocol: TCP
        port: 5428
        targetPort: 5428

И затем:

kind: Endpoints
  metadata:
    name: timewebapp-without-ep
  subsets:
    - addresses:
        - ip: x.x.x.x #укажите актуальный IP
      ports:
      - port: 5428

Это позволит установить имя timewebapp-without-ep для подключения к серверу timewebapp.

Службы Kubernetes

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

ClusterIP

Предназначен для внутрикластерного взаимодействия приложений. Настраивается это так (значения приложения даны случайные, их следует заменить на свои):

kind: Service #обязательная строчка для определения любой службы
  metadata:
    name: timewebapp
  spec:
    type: ClusterIP
    selector:
      app: timewebapp
    ports:
      - protocol: TCP
        port: 5428
        targetPort: 5428

NodePort

Внешняя служба для сопоставления подов с хостами через постоянный порт, прописываемый отдельно ниже (все значения тоже даны случайные, замените их на свои):

kind: Service
  metadata:
    name: timewebapp
  spec:
    type: NodePort
    selector:
      app: timewebapp
    ports:
      - protocol: TCP
        port: 5428
        targetPort: 5428
        nodePort: 32157

LoadBalancer

Служба для работы в облачной инфраструктуре. Позволяет обеспечить маршрутизацию, например, через сайт. Вот код запуска:

kind: Service
  metadata:
    name: timewebapp
  spec:
    type: LoadBalancer
    selector:
      app: timewebapp
    ports:
      - protocol: TCP
        port: 5428
        targetPort: 5428

ExternalName

Эта служба нужна для обеспечения внекластерного доступа. Делается это просто:

metadata:
  name: timewebapp
spec:
  type: ExternalName
  externalName: timewebapp.mydomain.ru

Далее отметим, что у любой службы создается имя DNS по такому шаблону: название-службы.пространство-имен.svc.cluster.local. Такая запись будет указывать на IP кластера. А при ее отсутствии Kube будет обращаться к IP отдельных подов.

Разновидности балансировки через службы Kube

Как мы убедились по описаниям всех четырех служб Kube, организовать балансировку нагрузки можно по-разному. Начнем с описания того, как это делается внутри кластера.

Внутрикластерная балансировка

Для внутрикластерной балансировки предназначена служба ClusterIP (код для настройки этой и других служб Kube мы публиковали выше в их описаниях). Она подойдет, например, для организации взаимодействия отдельных групп подов, расположенных в пределах одного кластера Kube. Организовать доступ к службе можно организовать двумя способами: через DNS или при помощи переменных окружения.

Про то, как это делается с помощью DNS, было сказано выше. Добавим, что такая организация доступа — это наиболее распространенный и рекомендуемый способ взаимодействия микросервисов. Однако учтите, что DNS работают в Kube только с надстройкой DNS-сервера: например, CoreDNS.

Что касается переменных среды, то они устанавливаются при запуске нового пода через инструкцию service-name. Вам могут понадобиться переменные PORT и SERVICE_HOST, а вот инструкции для их установки:

service-name_PORT
service-name_SERVICE_HOST

Внешняя балансировка

Она может выполняться с использованием NodePort (далее NP) и LoadBalancer (далее LB). NP подходит для балансировки ограниченного числа служб, а его преимущество заключается в обеспечении подключения без выделенного внешнего инструмента балансировки.

Первое ограничение NP: он подходит только для частной сети, а вот интернет-подключение через NP организовать не получится. Еще один недостаток NP в том, что он работает только через статические порты и в ограниченном диапазоне, причем служба должна выделять один и тот же порт для каждого узла. Это становится проблематичным, когда приложение масштабируется до нескольких микросервисов.

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

Давайте посмотрим, как создать внешний LB. В примере ниже мы запустим под и подключим его графический интерфейс из внешней сети. Обратите внимание, что LB не фильтрует входящий или исходящий трафик. Это просто прокси для связи с внешней сетью, перенаправляющий трафик на соответствующие модули/сервисы. Создаем кластер командой create cluster и прописываем следующие параметры (значения name и region приведены для примера, а в поле ssh-public-key введите свой публичный ключ SSH).

--name myTimewebCluster 
--region my-timeweb-region 
--with-oidc 
--ssh-access 
--ssh-public-key <xxxxxxxxxx> 
--managed

О том, как сгенерировать ключи SSH, смотрите в нашем руководстве. 

Теперь редактируем yaml пода:

kind: Pod
metadata:
name: timewebpod
labels:
app: timewebpod
spec:
containers:
- name: timewebpod
image: timewebpod:latest

Затем создаем под:

kubectl apply -f timewebpod.yaml

Проверяем работоспособность:

kubectl get pods --selector='app=timewebpod'

И подключаем LB (значения в образцах кода случайные, замените их на актуальные):

kind: Service
  metadata:
    name: timewebpod-ext-serv
  spec:
    type: LoadBalancer
    selector:
      app: timewebpod
    ports:
      - name: timewebpod-admin
        protocol: TCP
        port: 14953
        targetPort: 14953

Далее стартуем сервис и подтверждаем:

kubectl apply -f timewebpod-svc.yaml
service/timewebpod-ext-serv created
kubectl get svc

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

http://b9f305e6d743a85cb32f48f6a210cb51.my-timeweb-region.ru

Тогда нужно вставить в браузер следующее:

http://b9f305e6d743a85cb32f48f6a210cb51.my-timeweb-region.ru: 14953

Теперь вы сможете поделиться этим адресом с любым, кто желает подключиться к вашей записи администратора. Как видим, создать и настроить внешний балансировщик для приложения довольно легко. Правда, у Load Balancer есть некоторые ограничения, но их помогает обойти Ingress.

Балансировка через Ingress

Мы видели, что LoadBalance создает экземпляры приложения для каждой службы. Это нормально, пока у нас есть несколько служб, однако с увеличением служб управлять ими становится сложно. Кроме того, LB не поддерживает маршрутизацию URL, SSL и многое другое. И здесь на помощь приходит Ingress, который является расширением для NP и LB. В задачи Ingress входит обработка внутреннего трафика с целью определить, какие поды или службы пересылать дальше. Основной функцией Ingress является балансировка нагрузки, однако с его помощью можно также выполнять маршрутизацию URL, терминацию SSL и ряд других функций. Конфигураций Ingress довольно много, и их нетрудно найти по запросам. Приведем одну в качестве иллюстрации:

Image1

В приведенном примере определены правила, по которым будет идти трафик от конечных пользователей. Нужно также добавить, что Ingress не является службой Kube, как, например, LB или NP, а представляет собой набор правил, используемый этими службами. Кроме того, кластеру, использующему Ingress, необходим Ingress Controller. Этих контроллеров создано довольно много, а ознакомиться с некоторыми популярными решениями вы можете по этим ссылкам: AWS ALB, NGINX, Istio, Traefik.

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

Заключение

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

Зарегистрируйтесь и начните пользоваться
сервисами Timeweb Cloud прямо сейчас

15 лет опыта
Сосредоточьтесь на своей работе: об остальном позаботимся мы
165 000 клиентов
Нам доверяют частные лица и компании, от небольших фирм до корпораций
Поддержка 24/7
100+ специалистов поддержки, готовых помочь в чате, тикете и по телефону