Как отследить pod-to-pod-трафик Kubernetes
Эта статья для тех, кто ищет простое и понятное введение в организацию и управление кластерами Kubernetes или же хочет оптимизировать свой проект. Мы поговорим о тонкостях сетевых технологий Kubernetes и рассмотрим некоторые принципы и те механизмы, которые управляют взаимодействием между подами. Сетевую модель мы будем рассматривать, используя схему VirtualBox. Изучив настройки этого решения, предлагаемого пользователям по умолчанию, вы получите ясное представление о том, как осуществляется связь между подами в среде Kubernetes и сможете применить полученные знания на практике, так всё написанное будет подкреплено примерами. Но сначала немного теории.
Два стандарта Kubernetes
Вместо жесткой сетевой реализации в Kubernetes используются стандарты CRI и CNI, предлагающие собственные плагины с множеством вариантов настройки кластера Kubernetes. Таким образом вместо единого инфраструктурного решения предлагается весьма широкий набор альтернативных. И каждое такое решение позволяет реализовать уникальную структуру кластерной сети. Это позволяет выбрать тот подход, который лучше всего соответствует вашим требованиям с учетом масштабируемости, производительности, отказоустойчивости, а также совместимости с вашей инфраструктурой. Конечно, такая вариативность несет с собой и ряд проблем с конкретными сетевыми решениями. Но при правильной настройке вы обеспечите оптимальную производительность своей сети, надежную связь и удобное управление своими кластерами.
Добавим, что оба стандарта предлагают несколько популярных решений. Если говорить о CRI, то это прежде всего containerd и cri-o, а у CNI есть Calico и Flannel. Все они предоставляют необходимые функции для сред выполнения контейнеров, позволяя вам выбирать параметры, которые лучше всего соответствуют вашим требованиям и предпочтениям. При этом данные плагины чаще всего не исключают, а дополняют друг друга. Разумеется, такие сочетания требуют некоторых усилий в плане конфигурирования, чтобы гарантировать стабильную работу всей сборки. И эту задачу существенно облегчают дистрибутивы, в которых чаще всего плагины CRI и CNI уже настроены должным образом для того, чтобы пользователь мог сразу же приступить непосредственно к настройке приложений Kubernetes.
Из платных дистрибутивов можно отметить, например, Red Hat Openshift, но есть и достаточно функциональные бесплатные продукты, и прежде всего это OKD, minikube, а также areguera-k8s-lab, на примере которого мы и будем учиться отслеживать межподовый трафик.
О кластерной архитектуре
В vagrant-k8-lab
вшиты containerd
и Flannel на каждой ноде, что обеспечивает такую структуру:
Источник изображения: medium.com
Здесь следует отметить, что к каждому узлу в данном случае подключено сразу 4 интерфейса, eth0
, eth1
, cni0
и lo
. Каждый из них имеет свою цель, и сейчас мы их подробно разберем. А еще вы наверняка заметили, что в кластере уже развернуты несколько подов, которые также понадобятся нам для проведения последующих тестов.
Трафик нода-интернет, eth0
Чтобы иметь возможность загружать образы контейнеров извне, плагину CRI требуется исходящий трафик. Менеджеру пакетов ОС также нужно получать доступ к удаленным репозиториям. Когда ноды Kubernetes развертываются с помощью VirtualBox, то eth0
там подключается по умолчанию в режиме NAT. Гостевая ОС получает IP 10.0.2.15 и обменивается данными с VirtualBox через IP 10.0.2.3. Это настройки по умолчанию, которые обеспечивают взаимодействие виртуальных машин с «внешним миром» при условии, что у хоста с работающим VirtualBox, есть необходимое подключение. Вот как это выглядит схематично:
Источник изображения: medium.com
Конечно, схема сети VirtualBox по умолчанию может показаться не совсем логичной, но только до тех пор, пока вы не поймете, что виртуальные машины подключены к разным сетям, даже если все они используют одно и то же адресное пространство. Этот выбор конструкции VirtualBox позволяет осуществлять связь с внешними ресурсами, используя согласованную схему IP-адресации на всех виртуальных машинах.
В сетевой схеме VirtualBox по умолчанию соединения между виртуальными машинами обрабатываются индивидуально. Каждая виртуальная машина подключена к собственной изолированной сети 10.0.2.0/24 внутри VirtualBox, используя уникальный виртуальный канал для каждой внутренней сети. Таким образом, виртуальные машины взаимодействуют с внешним миром, но не могут взаимодействовать между собой.
Теперь давайте создадим под с использованием образа:
[vagrant@node-1 ~]$ kubectl run nginx --image=nginx:latest
Затем посмотрим на его статус:
[vagrant@node-1 ~]$ kubectl get pods/nginx
NAME READY STATUS RESTARTS AGE
nginx 1/1 Running 0 10m
Если статус Running, значит, всё в порядке и под активен на ноде 1, если, конечно, эта нода была выбрана в планировщике Kubernetes. Чтобы проверить это, вводим последовательно:
[vagrant@node-1 ~]$ kubectl describe pods/nginx
[vagrant@node-1 ~]$ journalctl -f -u containerd
Трафик нода-нода, eth1
Для подключения нод к одному внешнему каналу необходимо их прямое соединение. Поэтому eth0
не подходит для этой цели, и нужно использовать другой тип сетевого подключения. Здесь нас выручает eth1
, и реализуется вот такая простая и эффективная схема:
Источник изображения: medium.com
Как видим, для каждой ноды задан свой IP в определенном диапазоне, начиная с 192.168.56.10/24 для главной, 192.168.56.11/24 для первой рабочей ноды и 192.168.56.12/24 — для второй.
Теперь давайте проверим параметры подключения для обеих рабочих нод (в терминологии Kubernetes они называются workers), последовательно вводя следующие команды:
[vagrant@controlplane ~]$ traceroute -n 192.168.56.11
[vagrant@controlplane ~]$ traceroute -n 192.168.56.12
В случае успешного подключения вы увидите количество переданных пакетов и время их передачи в миллисекундах.
Трафик между подами в пределах одной ноды, cni0
Межподовый трафик возникает при запуске приложений Kubernetes на одном узле. В этом случае подам для обмена данными необходимо иметь уникальные IP. При этом вы знаете, что поды имеют определенный срок существования и уничтожаются так же легко, как и создаются. Это реализуется с помощью плагина CNI, а схематично выглядит так:
Источник изображения: medium.com
Теперь давайте создадим под из образа:
[vagrant@node-1 ~]$ kubectl run httpd --image=httpd
Далее нам нужно узнать IP созданного пода, что делается при помощи:
[vagrant@controlplane ~]$ kubectl get pods -o wide
Теперь через образ busybox
мы создаем новый под, подставив вместо IP полученное значение:
[vagrant@node-1 ~]$ kubectl run traceroute --image=busybox -- traceroute IP
И, наконец, проверяем параметры подключения, введя следующую команду для вывода лога:
[vagrant@node-1 ~]$ kubectl logs traceroute
Если в логе будет указан только один переход при определении маршрутизации (traceroute
), значит, мы имеем дело с cni0
, то есть трафик между подами осуществляется в пределах одной ноды.
Межнодовый трафик pod-to-pod
Одноузловые кластеры Kubernetes хороши в тестировании, однако при использовании в реальных проектах они не обеспечивают должного уровня отказоустойчивости. Ведь стоит единственной ноде отключиться, как рушится и всё приложение. Поэтому разработчики создают многоузловые кластеры, в которых нагрузка между нодами нередко распределяется равномерно. Для этой цели используется плагин Flanel, обеспечивающий «умную» маршрутизацию трафика без необходимости контролировать процесс маршрутизации вручную. А вот как выглядит многоузловой кластер:
Источник изображения: medium.com
Создаем под из образа и при этом проверяем, что под будет размещен на соответствующей ноде (в нашем случае на первой рабочей):
[vagrant@controlplane ~]$ kubectl run httpd --image=httpd \
--overrides= '{"spec": { "nodeSelector": {"kubernetes.io/hostname": "node-1"}}}'
Далее вводим еще одну знакомую команду:
[vagrant@controlplane ~]$ kubectl get pods -o wide
Теперь, снова как в прошлом примере, через образ busybox
мы создаем новый под, только на второй рабочей ноде, подставив вместо IP полученное значение:
[vagrant@controlplane ~]$ kubectl run traceroute --image=busybox \
--overrides='{"spec": { "nodeSelector": {"kubernetes.io/hostname": "node-2"}}}' \
-- traceroute IP
И в завершение выполняем команду для отслеживания маршрутизации:
[vagrant@controlplane ~]$ kubectl logs traceroute
Если всё работает корректно, то мы увидим 3 перехода, соответствующие схеме cni0
-eth1
-cni0
.
Заключение
Итак, мы с вами рассмотрели, как с помощью стандартных инструментов Kubernetes и подключаемого дистрибутива можно легко управлять конфигурацией кластера, создавая различные схемы и организуя трафик между подами в пределах одной или нескольких нод, то есть узлов кластера.