- PVSM.RU - https://www.pvsm.ru -
Прим. перев.: Автор статьи — Amanpreet Singh — называет себя «всё ещё начинающим в мире сетей», однако именно это и побудило его разобраться в их базовом устройстве в Kubernetes (который он использует в production), а затем — поделиться с сообществом очень доступным материалом с наглядными иллюстрациями. В оригинале он разбит на две [1] части [2], однако в этом переводе мы объединили их в одну статью.

Вот вы запустили множество сервисов в кластере Kubernetes и пожинаете плоды… или хотя бы собираетесь это сделать. Однако, даже несмотря на существование ряда утилит для настройки кластера и управления им, вам всё же интересно, как всё работает «под капотом». Куда смотреть, если что-то сломается? По себе знаю, что это важно.
С Kubernetes достаточно просто начинать работу. Но если посмотреть внутрь, там окажется сложная система. В ней множество «подвижных» компонентов, функционирование и взаимодействие которых необходимо понимать, если вы хотите подготовиться к возможным сбоям. Одной из наиболее сложных и, возможно, наиболее критичных составляющих Kubernetes является сеть.
Поэтому я решил разобраться, как именно она работает: прочитал документацию, послушал доклады и даже просмотрел кодовую базу — и вот что я выяснил…
В основе сетевого устройства Kubernetes — важный архитектурный принцип: «У каждого пода свой уникальный IP».
IP пода делится между всеми его контейнерами и является доступным (маршрутизируемым) для всех остальных подов. Замечали когда-нибудь на своих узлах работающие pause-контейнеры? Их ещё называют «контейнерами-песочницами» (sandbox containers), потому что их работа заключается в резервировании и удержании сетевого пространства имён (netns), используемого всеми контейнерами пода. Благодаря этому IP пода не меняется даже в тех случаях, когда контейнер умирает и вместо него создаётся новый. Большим достоинством такой модели — IP для каждого пода (IP-per-pod) — является отсутствие коллизий IP/портов на нижележащем хосте. А нам не нужно беспокоиться о том, какие порты используют приложения.
Поэтому единственное требование Kubernetes — все эти IP-адреса подов должны быть доступны/маршрутизируемы из остальных подов вне зависимости от того, на каком узле они расположены.
Первый шаг — удостовериться, что поды одного узла способны общаться между собой. Затем эта идея расширяется до взаимодействия между узлами, с интернетом и т.п.
На каждом узле Kubernetes, которым в данном случае является Linux-машина, существует корневое сетевое пространство имён — root netns. Основной сетевой интерфейс — eth0 — находится в этом root netns:

Аналогичным образом у каждого пода есть свой netns с виртуальным интерфейсом Ethernet, связывающим их с root netns. По сути это виртуальный линк с одним концом в root netns и другим — в netns пода.

Конец на стороне пода назван eth0, потому что под не знает о нижележащем хосте и думает, что у него своя корневая сетевая конфигурация. Другой конец назван как-нибудь вроде vethxxx. Вы можете увидеть все эти интерфейсы на своём узле Kubernetes, воспользовавшись командой ifconfig или ip a.
Таково устройство всех подов на узле. Для того, чтобы поды могли общаться друг с другом, используется Ethernet-мост Linux — cbr0. Docker использует похожий мост под названием docker0.

Вывести список мостов можно командой brctl show.
Предположим, пакет отправляется из pod1 в pod2:
eth0 покидает netns, принадлежащий pod1, и попадает в root netns через vethxxx.cbr0, который выдаёт ему точку назначения с помощью ARP-запроса, спрашивающего: «У кого такой IP-адрес?».vethyyy отвечает, что у него нужный IP — так мост узнаёт, куда переслать пакет.vethyyy и, проходя виртуальный линк, попадает в netns, принадлежащий pod2.

Так контейнеры одного узла общаются между собой. Очевидно, есть и другие способы взаимодействия, но этот, пожалуй, самый простой; его же использует и Docker.
Как упоминалось выше, поды также должны быть доступны из всех узлов. И для Kubernetes вовсе не принципиально, как это реализовано. Посему можно использовать L2 (ARP между узлами), L3 (IP-маршрутизация между узлами — аналогично таблицам роутинга у облачных провайдеров), оверлейные сети и даже почтовых голубей. Каждому узлу назначается уникальный блок CIDR (диапазон IP-адресов) для IP-адресов, выдаваемых подам, так что у каждого пода свой уникальный IP, не конфликтующий с подами других узлов.
В большинстве случаев, особенно в облачных окружениях, облачный провайдер использует таблицы маршрутизации, чтобы гарантировать, что пакеты доходят до корректных получателей. То же самое можно настроить с помощью маршрутов на каждом узле. Также есть множество других сетевых плагинов, решающих свои задачи.
Рассмотрим пример с двумя узлами, аналогичный тому, что был выше. У каждого узла есть различные сетевые пространства имён, сетевые интерфейсы и мост.

Предположим, пакет следует из pod1 на pod4 (на другом узле):
eth0 покидает netns, принадлежащий pod1, и попадает в root netns через vethxxx.cbr0, который делает ARP-запрос в поисках точки назначения.cbr0 переходит в основной сетевой интерфейс eth0, поскольку ни у кого на этом узле нет IP-адреса, соответствующего pod4.node1, оставаясь в сетевом проводе со значениями src=pod1 и dst=pod4.pod4.node2 — eth0. Теперь, хотя pod4 и не является IP-адресом eth0, пакет перенаправляется на cbr0, поскольку на узлах включён IP forwarding. Таблица маршрутизации узла просматривается на наличие маршрутов, соответствующих IP-адресу pod4. В ней обнаруживается cbr0 как точка назначения для блока CIDR этого узла. Посмотреть таблицу маршрутизации узла можно с помощью команды route -n — она покажет маршрут для cbr0 вроде такого:

vethyyy.pod4.Оверлейные сети не требуются по умолчанию, однако они полезны в некоторых ситуациях. Например, когда нам не хватает пространства IP-адресов или сеть не может управлять дополнительными маршрутами. Или когда мы хотим получить дополнительные возможности управления, предоставляемые оверлеями. Частый случай — наличие ограничения на количество маршрутов, поддерживаемых в таблицах роутинга облачного провайдера. Например, для таблицы маршрутизации в AWS заявлена поддержка до 50 маршрутов без влияния на производительность сети. Если нам потребуется более 50 узлов Kubernetes, таблицы маршрутизации AWS перестанет хватать. В таких случаях поможет оверлейная сеть.
Оверлейная сеть инкапсулирует пакеты, проходящие по сети между узлами. Возможно, вы не захотите её использовать из-за того, что инкапсуляция-декапсуляция всех пакетов добавляет небольшую задержку и сложность. Зачастую это не нужно, что стоит учитывать, принимая решение об их использовании.
Чтобы понять, как ходит трафик в оверлейной сети, рассмотрим пример с flannel [3] — Open Source-проектом от CoreOS:

Здесь мы видим конфигурацию, аналогичную предыдущей, однако в ней появилось новое виртуальное Ethernet-устройство под названием flannel0 — оно находится в корневом пространстве имён (root netns). Это реализация Virtual Extensible LAN (VXLAN), которая для Linux — просто ещё один сетевой интерфейс.
Прохождение пакета из pod1 в pod4 (он находится на другом узле) выглядит примерно так:
eth0 покидает netns, принадлежащий pod1, и оказывается в root netns на vethxxx.cbr0, который делает ARP-запрос для обнаружения точки назначения.pod4, мост отправляет пакет в flannel0 — таблица маршрутизации узла настроена на использованиее flannel0 в качестве цели для сетевого диапазона пода.flannel0 берёт пакет и заворачивает его в UDP-пакет с дополнительными заголовками, изменяющими IP-адреса источника и получателя на соответствующие узлы, отправляет его на специальный порт vxlan (обычно 8472):

Хотя сопоставления находятся в пользовательском пространстве, реальная инкапсуляция и прохождение данных происходит в пространстве ядра, так что это достаточно быстро.
eth0, поскольку он отвечает за роутинг трафика узла.node2.eth0 узла node2. Поскольку в качестве порта используется специальный vxlan — ядро отправляет пакет на flannel0.flannel0 декапсулирует пакет и переносит его обратно в root netns. Пакет покидает узел с IP-адресами узлов в качестве источника и назначения. Дальнейший путь совпадает с тем, что был в случае обычной (неоверлейной) сети.cbr0 согласно таблице маршрутизации.vethyyy.pod4.У различных реализаций могут быть незначительные отличия, но в целом именно так работают оверлейные сети в Kubernetes. Существует распространенное заблуждение, что их использование в Kubernetes необходимо, однако правда в том, что всё зависит от конкретных случаев. Так что сначала убедитесь, что применяете их только в случае реальной необходимости.
Читайте также в нашем блоге:
Автор: Wimbo
Источник [10]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/seti/275398
Ссылки в тексте:
[1] две: https://medium.com/@ApsOps/an-illustrated-guide-to-kubernetes-networking-part-1-d1ede3322727
[2] части: https://medium.com/@ApsOps/an-illustrated-guide-to-kubernetes-networking-part-2-13fdc6c4e24c
[3] flannel: http://github.com/coreos/flannel
[4] Сравнение производительности сетевых решений для Kubernetes: https://habrahabr.ru/company/flant/blog/332432/
[5] Container Networking Interface (CNI) — сетевой интерфейс и стандарт для Linux-контейнеров: https://habrahabr.ru/company/flant/blog/329830/
[6] часть 1: https://habrahabr.ru/company/flant/blog/342658/
[7] часть 2: https://habrahabr.ru/company/flant/blog/342822/
[8] Как на самом деле работает планировщик Kubernetes?: https://habrahabr.ru/company/flant/blog/335552/
[9] Инфраструктура с Kubernetes как доступная услуга: https://habrahabr.ru/company/flant/blog/341760/
[10] Источник: https://habrahabr.ru/post/346304/?utm_campaign=346304
Нажмите здесь для печати.