Липкие сессии для самых маленьких [Часть 2], или Как понять Kubernetes и преисполниться в своём познании

Kate

Administrator
Команда форума
Липкие сессии (Sticky-session) — это особый вид балансировки нагрузки, при которой трафик поступает на один определенный сервер группы. Как правило, перед группой серверов находится балансировщик нагрузки (Nginx, HAProxy), который и устанавливает правила распределения трафика на доступные сервера.
В первой части цикла мы уже разобрали как создавать липкие сессии с помощью Nginx. Во второй части разберем создание подобной балансировки средствами Kubernetes.
Так как статьи в основном направлены на начинающих - придется коснуться основ kubernetes. Да-да, я знаю в интернете полно материала для изучения куба. Но здесь будет минимум душной теории и максимум практики. Лучше один раз развернуть тестовое приложение в кластере и понять основные принципы, чем читать тонну скучных мануалов.
Кто просто хочет узнать про реализацию липкой сессии в кубе - тыкните сюда.
Kubernetes (K8s) - это открытое программное обеспечение для автоматизации развёртывания, масштабирования и управления контейнеризированными приложениями.
Минимальный кластер куба состоит из двух узлов (Node) - master и worker. Узлы в кластере - это машины (виртуальные машины, физические серверы), на которых работают ваши приложения. Master узел представляет собой мозг kubernetes, там происходит управление всем кластером. Приложения, как правило, на мастер узле не разворачиваются (это конечно можно сделать, но это частный случай и выходит за рамки статьи). А вот на worker узлах приложения и разворачиваются. Для учебных целей иметь две машины и ставить туда куб не очень-то удобно. Поэтому придумали minikube.
Minikube - это одноузловой кластер, который является сразу master и worker нодой и всё это на локальной машине. В нем есть некоторые упрощения по сравнению с реальным кубом, например установка ingress-controller или запуск борды. Инструмент нужен в основном для локальной разработки и обучения.
Все примеры будут под систему MacOS. Установку для других систем смотрите в официальной документации.
brew install minikube
Чтобы запустить minikube выполните команду:
minikube start
Работать с кубом можно через командную строку, для этого нужно установить kubectl.
minikube kubectl --get po -A
Эта утилитка ставится и для взаимодействия с реальным кубом. Чтобы посмотреть дашборд с графиками и картиночками выполните:
minikube dashboard
Только учтите, что команда заблокирует вам консоль, поэтому лучше откройте еще одно окно.
057560c0eb5073fcfbdd901709fac2ad.png
6cdd76c285e7f8969a6f84bd1b2c964d.png

На этом установка учебного кластера завершена. Как по мне, для понимания основ kubernetes нужно разобрать всего 4 ресурса:
  1. Pod
  2. Deployment
  3. Service
  4. Ingress
В таком порядке и будем рассматривать каждый ресурс по мере запуска приложения в кластере.
Подготовка приложения
Всё, мы готовы разворачивать приложение. Для этого нужно кубу указать ряд правил, которыми он будет руководствоваться при запуске вашего приложения. Такие правила называются конфигурацией развертывания (Deployments).
Обычно любой ресурс в Kubernetes описывается в yaml файлах. В нашем случае правил развертывания не много, поэтому обойдемся короткой командой по созданию объекта deployment.
kubectl create deployment sticky-d --image=mopckou/sticky-session:0.0.5
В -–image можете указать свое приложение, которое залили в публичный репозиторий docker hub.
fce38f5356813d9df5cd52aa51c573dd.png

При создании ресурса deployment, создается так же пода (Pod) на узле. Пода это минимальная единица куба, в котором запускается контейнер или множество контейнеров докера. Deployment отслеживает состояние и работоспособность под. Если какая выйдет из строя, то он оперативно запустит новую.
Проверим, что создалось два ресурса deployment и pod.
kubectl get deployment,pods -l app=sticky-d
23f5ea6c63d858f00d0379ce31cdbfa6.png
Про "kubectl get"
Масштабируем наше приложение до двух сервисов.
kubectl scale deployment sticky-d --replicas=2
И снова проверим состояние pods и deployment, вывод должен быть примерно такой:
f3a83f8c10ff355f913ff7a3ac05aade.png

Новая пода стала запускаться.
эксперименты с deployment
Теперь давайте отправим запросы на развернутые приложения. Напрямую это сделать не удастся, для этого придумали сервис (Service).
Сервис (Service) в Kubernetes — это абстрактный объект (ресурс), который предоставляет доступ к запущенным подам. Хотя у каждого пода есть уникальный IP-адрес, эти IP-адреса не доступны за пределами кластера без использования сервиса. Сервисы позволяют приложениям принимать трафик и являются стандартным балансировщиком нагрузки на поды.
Сервисы могут по-разному открыты, в зависимости от указанного поля type:
  • ClusterIP (по умолчанию) - открывает доступ к сервису по внутреннему IP-адресу в кластере. Этот тип делает сервис доступным только внутри кластера.
  • NodePort - открывает сервис на одном и том же порту каждого выбранного узла в кластере с помощью NAT. Делает сервис доступным вне кластера, используя NodeIP:NodePort.
  • LoadBalancer - создает внешний балансировщик нагрузки в текущем облаке (если это поддерживается) и назначает фиксированный внешний IP-адрес для сервиса.
  • ExternalName - открывает доступ к сервису с указанным именем (определённое в поле externalName в спецификации) и возвращает запись CNAME. Прокси не используется. Для этого типа требуется версия kube-dns 1.7 или выше.
Создадим сервис с типом LoadBalancer. Балансировка происходит по принципу random balancing (про это можно почитать в этой статье, которая ссылается сюда).
kubectl expose deployment sticky-d --type=LoadBalancer --port=8080
c6b618905547cc8b1e863f1e60aa61a1.png
kubectl get services sticky-d
e7395cd3872e72a330405f6fa23fd9bc.png

Запустить сервис можно командой:
minikube service sticky-d
Вывод будет примерно такой:
be2f2fb834b5f0a0eb2ed861e8ccd6f7.png

Отправим запросы на сервис и убедимся, что трафик поступает на разные поды:
0313f88506cb2c2030ec40f05ecce139.gif

Балансировщик не работает? Или мы добились своей цели и получили липкие сессии? Почти!
Мы получили липки сессии курильщика. Дело в том, пока открыто TCP соединение с сервером куб направляет трафик только на одну определенную поду. Но если будет обрыв соединения или мы создадим новое соединение, то можем попасть уже на другую поду. Подробнее об этом смотрите в этой отличной статье. Не такие липкие сессии нужны Готему сейчас. Чтобы обойти эту особенность необходимо убрать галочку keep-alive в Postman.
ae8181aed5253516629c6fc6136b13d6.png

Посмотрим, что произойдет:
6dacd0553023e23eafe7322f0534bfb7.gif

Ура! Мы убедились, что трафик балансируется.
Ресурс сервис довольно бедный балансировщик и умеет распределять трафик для определенного развертывания. Тут вступает ingress, который довольно сильно расширяет возможности балансировки в kubernetes.
Если просто создать ресурс ingress, то ничего не произойдет. Нужно создать ingress-controller. Для активации ingress-controller выполните:
minikube addons enable ingress
проблемы с активацией ingress-controller?
При успешном запуске Ingress-controller запустится специальная пода, выполните команду:
kubectl get pods -n kube-system
Результат должен примерно таким:
3ced297b38ef7b696ec9fcefd4711bdf.png
Подробно на простых примерах зачем нужен ingress
Теперь создадим ресурс Ingress. Сделайте новый файлик ingress.yml и вставьте следующий текст:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: example-ingress
spec:
rules:
- host: sticky.info
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: sticky-d
port:
number: 8080
Обратите внимание на строчку host: sticky.info Нужно локально определить доменное имя и присвоить ему ip адрес ingress ресурса. Перед тем как определять локально доменное имя запустим ingress ресурс.
kubectl apply -f ingress.yml
Все ресурсы описанные в yml файлах применяются командой apply. Проверим, появился ли ingress объект:
kubectl get ingresses
5f4f488f2cb103635c302cc576775651.png

Обратите внимание на адрес ресурса, если сделать запрос на 192.168.64.3:80 будет ошибка 404.
"Разрешим" локально доменное имя. Откройте файл /etc/hosts и в последней строчке через пробел запишите ip адрес ingress ресурса и доменное имя сервиса.
1bdb140e0dfc4819fdb6d4ab0d4be1ba.png

И попробуем отправить теперь запрос по адресу sticky.info:
441d396ae4cff56cc0f0d60d7c7d9107.gif

Надо отметить, что балансировка работает несмотря на включенную галочку connection.
небольшой хак
На этом завершается краткая вводная по основам kubernetes. Мы успешно развернули простенькое приложение в кластере и получили к нему доступ. Теперь можно глубже погружаться в теорию: например почитать про namespace (мы разворачивали приложение в default пространстве), почитать про остальные ресурсы куба и конечно вникнуть про создание объектов в формате yml.
Теперь наконец сделаем наш трафик липким. Добавим в Ingress.yml annotations c 4 параметрами:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: example-ingress
annotations:
nginx.ingress.kubernetes.io/affinity: "cookie"
nginx.ingress.kubernetes.io/affinity-mode: "balanced"
nginx.ingress.kubernetes.io/session-cookie-name: "key"
nginx.ingress.kubernetes.io/session-cookie-max-age: 60
spec:
rules:
- host: sticky.info
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: sticky-d
port:
number: 8080

Рассмотрим каждый параметр подробнее:
  • affinity: укажите значение “cookie”, чтобы включить липкие сессии;
  • affinity-mode: режим прилипания. Поддерживается два режима — persistent и balanced. При значении balanced трафик будет прилипать к определенному поду только на некоторый промежуток времени. Для вечного прилипания укажите persistent;
  • session-cookie-name: имя cookie, по значению которого ingress-controller будет ассоциировать трафик с определенной подой;
  • session-cookie-max-age: время, на которое трафик прилипает к поду, в секундах (настройка нужна если affinity-mode = balanced). После истечения заданного времени ingress-controller сгенеририт новое значение для session-cookie-name и вставит нам в cookie.
Применим изменения:
kubectl apply -f ingress.yml
И проверим работу липких сессий в кубе:
cf293c9d82d61e5339da2bec838b58fd.gif

Ответы будут приходить только от одного конкретного экземпляра приложения. Обратите внимание, что ingress-controller сам подставил нам в cookie значение для key и добавил срок годности прилипания. Через 60 секунд nginx-controller снова сгенерит значение для key, и трафик уже прилипнет к другой поде (но не факт, что к другой).
e1e33be46c39b794f2cb4bb968dd7382.png

Теперь посмотрим как это выглядит в коде:
import asyncio
from aiohttp import ClientSession
from asyncio import sleep


async def foo():
session = ClientSession()

while 1:
response = await session.get('http://sticky.info')
uuid = (await response.json())['uuid']

print(f"answer: {uuid} /// cookie: {response.cookies}")

await sleep(1)


lp = asyncio.get_event_loop()
lp.run_until_complete(foo())
lp.close()
В цикле делаем запрос к приложению и выводим результат запроса и объект cookie.
dbdbd26423b0fc7ba2dd2ba331a2a80d.png

Ingress-controller сгенерил key и подставил нам в cookie объект Set-Cookie. Трафик прилип на 60 секунд к определенной поде.
07ab73ca0159a67cce9f2d558c207ba8.png

Готово! Мы успешно реализовали липкие сессии в Nginx и Kubernetes.


Источник статьи: https://habr.com/ru/company/domclick/blog/551332/

rtrg
 
Сверху