Липкие сессии (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
Только учтите, что команда заблокирует вам консоль, поэтому лучше откройте еще одно окно.
На этом установка учебного кластера завершена. Как по мне, для понимания основ kubernetes нужно разобрать всего 4 ресурса:
Подготовка приложения
Всё, мы готовы разворачивать приложение. Для этого нужно кубу указать ряд правил, которыми он будет руководствоваться при запуске вашего приложения. Такие правила называются конфигурацией развертывания (Deployments).
Обычно любой ресурс в Kubernetes описывается в yaml файлах. В нашем случае правил развертывания не много, поэтому обойдемся короткой командой по созданию объекта deployment.
kubectl create deployment sticky-d --image=mopckou/sticky-session:0.0.5
В -–image можете указать свое приложение, которое залили в публичный репозиторий docker hub.
При создании ресурса deployment, создается так же пода (Pod) на узле. Пода это минимальная единица куба, в котором запускается контейнер или множество контейнеров докера. Deployment отслеживает состояние и работоспособность под. Если какая выйдет из строя, то он оперативно запустит новую.
Проверим, что создалось два ресурса deployment и pod.
kubectl get deployment,pods -l app=sticky-d
Про "kubectl get"
Масштабируем наше приложение до двух сервисов.
kubectl scale deployment sticky-d --replicas=2
И снова проверим состояние pods и deployment, вывод должен быть примерно такой:
Новая пода стала запускаться.
эксперименты с deployment
Теперь давайте отправим запросы на развернутые приложения. Напрямую это сделать не удастся, для этого придумали сервис (Service).
Сервис (Service) в Kubernetes — это абстрактный объект (ресурс), который предоставляет доступ к запущенным подам. Хотя у каждого пода есть уникальный IP-адрес, эти IP-адреса не доступны за пределами кластера без использования сервиса. Сервисы позволяют приложениям принимать трафик и являются стандартным балансировщиком нагрузки на поды.
Сервисы могут по-разному открыты, в зависимости от указанного поля type:
kubectl expose deployment sticky-d --type=LoadBalancer --port=8080
kubectl get services sticky-d
Запустить сервис можно командой:
minikube service sticky-d
Вывод будет примерно такой:
Отправим запросы на сервис и убедимся, что трафик поступает на разные поды:
Балансировщик не работает? Или мы добились своей цели и получили липкие сессии? Почти!
Мы получили липки сессии курильщика. Дело в том, пока открыто TCP соединение с сервером куб направляет трафик только на одну определенную поду. Но если будет обрыв соединения или мы создадим новое соединение, то можем попасть уже на другую поду. Подробнее об этом смотрите в этой отличной статье. Не такие липкие сессии нужны Готему сейчас. Чтобы обойти эту особенность необходимо убрать галочку keep-alive в Postman.
Посмотрим, что произойдет:
Ура! Мы убедились, что трафик балансируется.
Ресурс сервис довольно бедный балансировщик и умеет распределять трафик для определенного развертывания. Тут вступает ingress, который довольно сильно расширяет возможности балансировки в kubernetes.
Если просто создать ресурс ingress, то ничего не произойдет. Нужно создать ingress-controller. Для активации ingress-controller выполните:
minikube addons enable ingress
проблемы с активацией ingress-controller?
При успешном запуске Ingress-controller запустится специальная пода, выполните команду:
kubectl get pods -n kube-system
Результат должен примерно таким:
Подробно на простых примерах зачем нужен 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
Обратите внимание на адрес ресурса, если сделать запрос на 192.168.64.3:80 будет ошибка 404.
"Разрешим" локально доменное имя. Откройте файл /etc/hosts и в последней строчке через пробел запишите ip адрес ingress ресурса и доменное имя сервиса.
И попробуем отправить теперь запрос по адресу sticky.info:
Надо отметить, что балансировка работает несмотря на включенную галочку 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
Рассмотрим каждый параметр подробнее:
kubectl apply -f ingress.yml
И проверим работу липких сессий в кубе:
Ответы будут приходить только от одного конкретного экземпляра приложения. Обратите внимание, что ingress-controller сам подставил нам в cookie значение для key и добавил срок годности прилипания. Через 60 секунд nginx-controller снова сгенерит значение для key, и трафик уже прилипнет к другой поде (но не факт, что к другой).
Теперь посмотрим как это выглядит в коде:
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.
Ingress-controller сгенерил key и подставил нам в cookie объект Set-Cookie. Трафик прилип на 60 секунд к определенной поде.
Готово! Мы успешно реализовали липкие сессии в Nginx и Kubernetes.
Источник статьи: https://habr.com/ru/company/domclick/blog/551332/
В первой части цикла мы уже разобрали как создавать липкие сессии с помощью 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
Только учтите, что команда заблокирует вам консоль, поэтому лучше откройте еще одно окно.


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

При создании ресурса deployment, создается так же пода (Pod) на узле. Пода это минимальная единица куба, в котором запускается контейнер или множество контейнеров докера. Deployment отслеживает состояние и работоспособность под. Если какая выйдет из строя, то он оперативно запустит новую.
Проверим, что создалось два ресурса deployment и pod.
kubectl get deployment,pods -l app=sticky-d

Масштабируем наше приложение до двух сервисов.
kubectl scale deployment sticky-d --replicas=2
И снова проверим состояние pods и deployment, вывод должен быть примерно такой:

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


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

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

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

Посмотрим, что произойдет:

Ура! Мы убедились, что трафик балансируется.
Ресурс сервис довольно бедный балансировщик и умеет распределять трафик для определенного развертывания. Тут вступает ingress, который довольно сильно расширяет возможности балансировки в kubernetes.
Если просто создать ресурс ingress, то ничего не произойдет. Нужно создать ingress-controller. Для активации ingress-controller выполните:
minikube addons enable ingress
проблемы с активацией ingress-controller?
При успешном запуске Ingress-controller запустится специальная пода, выполните команду:
kubectl get pods -n kube-system
Результат должен примерно таким:

Теперь создадим ресурс 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

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

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

Надо отметить, что балансировка работает несмотря на включенную галочку 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
И проверим работу липких сессий в кубе:

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

Теперь посмотрим как это выглядит в коде:
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.

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

Готово! Мы успешно реализовали липкие сессии в Nginx и Kubernetes.
Источник статьи: https://habr.com/ru/company/domclick/blog/551332/