Задался я вопросом – а как вообще в Kubernetes происходит балансировка нагрузки между подами?
Т.е, есть у нас внешний Load Balancer. За ним – Service. За ним – Pod.
Что происходит, когда мы из мира получаем пакет, а у нас несколько подов – как пакеты между ними распределяются?
Содержание
kube-proxy
За правила маршрутизации пакетов между Service и Pod у нас отвечает сервис kube-proxy
, который может работать в одном из трёх режимов – user space proxy mode, iptables proxy mode и IPVS proxy mode.
User space proxy mode
Ссылки:
Устаревший режим, ранее был режимом по-умолчанию.
При использовании user space proxy mode, kube-proxy
отслеживает изменения в кластере и для каждого нового Service открывает TCP-порт на WorkerNode.
Далее, iptables
на этой WorkerNode роутит трафик с этого порта к kube-proxy
, который собственно проксирует пакеты к подам, используя режим round-robin, т.е. отправляя трафик на следующий в списке под. При этом, kube-proxy
имеет возможность отправить пакет к другому поду, если первый недоступен.
iptables proxy mode
Ссылки:
Наш случай, который и разберём в этом посте. Используется по-умолчанию.
При использовании iptables mode, kube-proxy
отслеживает изменения в кластере и для каждого нового Service открывает TCP-порт на WorkerNode.
Далее, iptables
на этой WorkerNode роутит трафик с этого порта к Kubernetes Service, который по сути является цепочкой в правилах iptables
, а через неё – напрямую к подам, которые являются бекендами этого сервиса, при этом поды выбираются рандомно.
Этот режим менее ресурсоёмкий для системы, так как все операции выполняются в ядре в модуле netfilter
, быстрее, и более надёжен, потому что нет “прослойки” в виде проксирующего сервиса – самого kube-proxy
.
Но если под, на который был направлен пакет, не отвечает – то такое соединение разрывается, тогда как в user space proxy mode – прокси попробовал бы другой под.
Вот, почему важно использовать и правильно настроить Readiness Probes – что бы Kubernetes не отправлял паакеты на такие поды.
Кроме того – такой режим сложнее дебажить, т.к. при user space proxy mode kube-proxy
пишет лог в /var/log/kube-proxy
, а в случае с netfilter
– придётся отслеживать работу самого ядра.
IPVS proxy mode
Ссылки:
И самый новый режим, использующий модуль ядра netlink
, и при создании новых Service создаёт соответсвующие правила PVS.
Главное преимущество – разнообразие вариантов балансировки нагрузки:
rr
: round-robinlc
: least connection (smallest number of open connections)dh
: destination hashingsh
: source hashingsed
: shortest expected delaynq
: never queue
kube-proxy
config
Проверим, какой режиму нас используется в AWS Elastic Kubernetes Service.
Находим поды:
На каждой WorkerNode кластера запущен свой инстанс kube-proxy
, к которым монтируется ConfigMap с именем kube-proxy-config:
Смотрим содержимое этого ConfigMap:
Теперь, когда мы рассмотрели режимы kube-proxy
– проверим, как оно работает, и как тут задействован iptables
.
Kubernetes Pod load-balancing
Для примера возьмём реальное приложение, у которого есть Ingress (AWS Application Load Balancer, ALB), который направляет трафик на Kubernetes Service:
Проверим сам Service:
Тип NodePort
– слушает TCP порт на WorkerNode.
AWS ALB e172ad3e-eksdev1appname-abac направляет трафик от клиентов на AWS TargetGroup e172ad3e-4caa286edf23ff7e06d:
ЕС2 в этой TargetGroup прослушивают порт 31103, который мы и видим в описании Service выше:
AWS LoadBalancer traffic modes
ALB поддерживает два типа трафика – IP, и Instance Mode.
- Instance mode: режим по-умолчанию, требует от Kubernetes Service типа NodePort, и направляет трафик на TCP-порт рабочей ноды
- IP mode: при этом режиме таргетами для ALB являются сами Kubernetes Pods, а не Kubernetes Worker Node.
Далее нам потребуется доступ к одной из рабочих нод – подключаемся на Bastion хост, и с него – к рабочей ноде:
kube-proxy
и iptables
Итак, наш пакет от клиента пришёл на рабочую ноду.
На ноде kube-proxy
биндит этот порт, что бы его не занял никакой другой сервис, и создаёт правила в iptables
:
Пакет приходит на порт 31107, где начинают работать правила фильтрации iptables
.
Правила iptables
Ссылки:
- https://www.frozentux.net/iptables-tutorial/chunkyhtml/c962.html
- https://upload.wikimedia.org/wikipedia/commons/3/37/Netfilter-packet-flow.svg
- https://www.digitalocean.com/community/tutorials/a-deep-dive-into-iptables-and-netfilter-architecture
Ядро принимает пакет, который попадает в цепочку (chain) PREROUTING
таблицы nat
:
См. describing kube-proxy iptables rules.
Проверяем правила в nat
таблице и её цепочке PREROUTING
:
Тут у нас target
== следующая цепочка, KUBE-SERVICES
, в которой последним правилом идёт следующая цепочка – KUBE-NODEPORTS
, в которую попадают пакеты для Service с типом NodePort
:
Проверяем правила этой цепочки:
Тут ловятся пакеты для dpt:31103
(destination port 31103) и отправляются в следующую цепочку – KUBE-SVC-TII5GQPKXWC65SRC
, проверяем её:
Тут следующие две цепочки, где собственно и происходит “магия” балансировки – пакет рандомно отправляется на одну из этих цепочек, 0.5 из 1.0 веса каждой – statistic mode random probability 0.5
, как и сказано в документации:
By default, kube-proxy in iptables mode chooses a backend at random.
См. также Turning IPTables into a TCP load balancer for fun and profit.
Проверяем эти цепочки:
И вторая:
Где цепочка DNAT (Destination NAT) отправляет пакет на IP к порту 3001, который является нашим ContainerPort
– проверим Deployment:
И проверим IP наших подов – находим поды:
Их IP, первый под:
И второй:
Всё логично? 🙂
Попробуем заскейлить деплоймент и посмотрим, как изменится правило в цепочке KUBE-SVC-TII5GQPKXWC65SRC
.
Находим деплоймент:
Скейлим его до трёх подов:
Проверяем iptables
:
Теперь у нас в цепочке KUBE-SVC-TII5GQPKXWC65SRC
три правила – у первого рандом 0.33332999982, т.к. есть три правила, в следущем правиле уже срабывает условие 0.5, и последнее – без правил.
См. iptables statistics module.
В целом, на этом всё.
Ссылки по теме
- Kubernetes: ClusterIP vs NodePort vs LoadBalancer, Services и Ingress — обзор, примеры
- Kubernetes Networking Demystified: A Brief Guide
- A Deep Dive into Iptables and Netfilter Architecture
- Traversing of tables and chains
- Turning IPTables into a TCP load balancer for fun and profit
- Cracking kubernetes node proxy (aka kube-proxy)
- A minimal IPVS Load Balancer demo
- How to enable IPVS mode on AWS EKS?
- Load-Balancing in Kubernetes