Хабрахабр

[Перевод] Сети Kubernetes: поды

Материал, перевод которого мы сегодня публикуем, посвящён особенностям сетевого взаимодействия подов Kubernetes. Он предназначен для тех, у кого уже есть некоторый опыт работы с Kubernetes. Если вы пока не очень хорошо разбираетесь в Kubernetes, то вам, вероятно, прежде чем читать этот материал, полезно будет взглянуть на это руководство по Kubernetes, где работа с данной платформой рассматривается в расчёте на начинающих.

Поды

Что такое под (pod) Kubernetes? Под — это сущность, которая состоит из одного или нескольких контейнеров, размещённых на одном хосте и настроенных на совместное использование ресурсов сетевого стека и других ресурсов наподобие томов. Поды — это базовые строительные блоки, из которых построены приложения, работающие на платформе Kubernetes. Поды совместно используют сетевой стек. На практике это означает, что все контейнеры, входящие в состав пода, могут взаимодействовать друг с другом через localhost. Если в поде есть контейнер, в котором выполняется nginx, прослушивающий порт 80, и ещё один контейнер, в котором выполняется scrapyd, то этот контейнер может обратиться к первому контейнеру по адресу http://localhost:80. Выглядит это не так уж и сложно. Зададимся теперь вопросом о том, как это, на самом деле, работает. Взглянем на типичную ситуацию, когда контейнер Docker запускают на локальной машине.

Контейнер Docker, запущенный на локальной машине

К нему присоединён мост docker0, а к мосту присоединён виртуальный сетевой интерфейс veth0. Если рассмотреть эту схему сверху вниз, то окажется, что здесь имеется физический сетевой интерфейс eth0. 17. Обратите внимание на то, что интерфейсы docker0 и veth0 находятся в одной и той же сети, в данном примере это 172. 0/24. 0. 17. В этой сети интерфейсу docker0 назначен IP-адрес 172. 1, этот интерфейс является шлюзом по умолчанию для интерфейса veth0, которому назначен адрес 172. 0. 0. 17. Из-за особенностей настройки сетевых пространств имён при запуске контейнера процессы внутри контейнера видят только интерфейс veth0 и взаимодействуют с внешним миром через интерфейсы docker0 и eth0. 2. Теперь запустим второй контейнер.

Два контейнера Docker, запущенные на локальной машине

Это — довольно сжатое описание того, что происходит на самом деле. Как можно видеть на вышеприведённой схеме, второму контейнеру назначается новый виртуальный сетевой интерфейс veth1, который подключён к тому же самому мосту, что и первый контейнер — к docker0. Подробности об этом можно найти здесь. Кроме того, надо отметить, что соединение между контейнером и мостом устанавливается благодаря паре связанных виртуальных интерфейсов Ethernet, один из которых находится в пространстве имён контейнера, а другой — в пространстве имён корневой сети.

К счастью, пространства имён отличаются большой гибкостью. Всё это хорошо, но это не описывает пока то, что мы, в применении к подам Kubernetes, называем «разделяемым сетевым стеком». При таком подходе нам придётся изменить вышеприведённую схему так, как показано ниже. Docker может запустить контейнер, и, вместо того, чтобы создавать для него новый виртуальный сетевой интерфейс, сделать так, чтобы он использовал бы, совместно с другими контейнерами, существующий интерфейс.

Контейнеры используют общий сетевой интерфейс

Применение такой схемы ведёт к нескольким следствиям. Теперь второй контейнер взаимодействует с уже существующим интерфейсом veth0, а не со своим собственным интерфейсом veth1, как это было в предыдущем примере. 17. Для начала, теперь можно сказать, что оба контейнера видны извне по одному адресу — 172. 2, а внутри каждый из них может обращаться к портам на localhost, открытым другим контейнером. 0. Это, конечно, ограничение, но оно не отличается от аналогичного ограничения, действующего в ситуации, когда на одном и том же хосте работают несколько процессов, открывающих порты. Кроме того, это означает, что эти контейнеры не могут открывать одни и те же порты. При таком подходе набор процессов получает все преимущества, связанные с выполнением этих процессов в контейнерах, такие, как слабая связанность и изоляция, но, в то же время, процессы могут организовывать совместную работу в простейшем из существующих сетевых окружений.

Если подключиться к узлу кластера Kubernetes, которому назначен конкретный под, по ssh, и выполните команду docker ps, то вы увидите как минимум один контейнер, запущенный с командой pause. Kubernetes реализует этот паттерн, создавая для каждого пода специальный контейнер, единственной целью которого является предоставление сетевого интерфейса для других контейнеров пода. Такие контейнеры совершенно ничего не делают, они находятся в «спящем» состоянии и ожидают получения этого сигнала. Эта команда приостанавливает текущий процесс до поступления сигнала SIGTERM. В результате оказывается, что в гипотетической среде, напоминающей под, наша предыдущая схема выглядела бы так, как показано ниже. Несмотря на то, что «приостановленные» контейнеры ничего не делают, они являются, так сказать, «сердцем» пода, предоставляя другим контейнерам виртуальный сетевой интерфейс, которым они могут пользоваться для взаимодействия друг с другом или с внешним миром.

Контейнеры в гипотетическом поде

Сеть подов

Один под, полный контейнеров, это строительный блок некоей системы, но пока ещё не сама эта система. В основе архитектуры Kubernetes лежит требование, в соответствии с которым у подов должна быть возможность взаимодействовать с другими подами вне зависимости от того, выполняются ли они на одном и том же компьютере или на разных машинах. Для того чтобы узнать о том, как всё это устроено, нам нужно перейти на более высокий уровень абстракции и поговорить о том, как в кластере Kubernetes работают узлы. Здесь мы затронем тему сетевой маршрутизации и маршрутов. Данной темы в материалах, подобных этому, нередко избегают, считая её слишком сложной. Непросто найти понятное и не слишком длинное руководство по IP-маршрутизации, но если вам хочется взглянуть на краткий обзор этой проблемы — можете взглянуть на этот материал.

Узел — это хост-система, физическая или виртуальная, которая содержит разные программные средства и их зависимости (речь идёт, в основном, о Docker), а также несколько системных компонентов Kubernetes. Кластер Kubernetes состоит из одного узла или из большего количества узлов. Вот как может выглядеть простой кластер, состоящий из двух узлов. Узел подключён к сети, что позволяет ему обмениваться данными с другими узлами кластера.

Простой кластер, состоящий из двух узлов

В демонстрационных целях в данном примере использована частная сеть 10. Если кластер, о котором идёт речь, работает в облачной среде наподобие GCP или AWS, то эта схема довольно точно передаёт сущность используемой по умолчанию для отдельных проектов сетевой архитектуры. 0. 100. В результате маршрутизатору назначен адрес 10. 0/24. 0. 100. 100. 1, а двум узлам — адреса 10. 2 и 10. 0. 0. 100. При использовании такой архитектуры каждый из узлов может взаимодействовать с другим с использованием своего сетевого интерфейса eth0. 3. Он подключён к мосту в совершенно другой сети. Теперь давайте вспомним о том, что под, запущенный на хосте, находится не в этой частной сети. Для того чтобы было понятнее, давайте перерисуем предыдущую схему, добавив на неё то, что выше мы называли гипотетическим подом. Это — виртуальная сеть, которая существует только в пределах конкретного узла.

Поды и узлы

100. Хост, расположенный слева на этой схеме, имеет интерфейс eht0 с адресом 10. 2, шлюзом по умолчанию для которого является маршрутизатор с адресом 10. 0. 0. 100. К этому интерфейсу подключён мост docker0 с адресом 172. 1. 0. 17. 17. 1, а к нему, через виртуальный интерфейс veth0 с адресом 172. 2, подключено то, что мы тут называем подом. 0. Он видим во всех трёх контейнерах посредством разделяемого сетевого стека. Интерфейс veth0 был создан в приостановленном контейнере. 17. Из-за того, что локальные правила маршрутизации настроены при создании моста, любой пакет, поступающий на eth0 и имеющий целевой адрес 172. 2, будет перенаправлен мосту, который перешлёт его на виртуальный интерфейс veth0. 0. Если известно, что на обсуждаемом нами хосте есть под с адресом 172. Пока всё это выглядит вполне прилично. 0. 17. 100. 2, то мы можем добавить правило в настройки маршрутизатора, описывающее то, что следующим переходом для этого адреса является 10. 2, после чего пакеты оттуда должны перенаправляться на veth0. 0. Теперь давайте взглянем на другой хост. Превосходно.

100. У хоста, изображённого на схеме справа, имеется физический интерфейс eth0 с адресом 10. 3. 0. 100. Он использует тот же самый шлюз по умолчанию — 10. 1, и, опять же, подключён к мосту docker0 с адресом 172. 0. 0. 17. Тут возникает ощущение, что всё идёт не так уж и хорошо. 1. Адреса мостов тут сделаны одинаковыми для того, чтобы продемонстрировать наихудший из возможных вариантов развития событий, который, например, может проявиться, если вы только что установили Docker и позволили ему работать так, как ему заблагорассудится. Этот адрес, на самом деле, может и отличаться от того, который используется на хосте, расположенном слева. А нам об этом нужно знать — для того, чтобы иметь возможность отправлять этим мостам пакеты и быть уверенными в том, что они придут туда, куда нужно. Но если даже сети, о которых идёт речь, различаются, наш пример высвечивает более глубокую проблему, которая заключается в том, что узлы обычно ничего не знают о том, какие частные адреса назначены мостам, находящимся в других узлах. Очевидно, что тут нужна некая сущность, которая позволяет обеспечивать правильную настройку адресов в разных узлах.

Во-первых, эта платформа назначает общее адресное пространство для мостов в каждом узле и затем назначает мостам адреса, находящиеся в этом пространстве, основываясь на том, в каком узле находится мост. Платформа Kubernetes даёт нам решение этой проблемы, состоящее из двух шагов. 100. Во-вторых, Kubernetes добавляет правила маршрутизации в шлюз, находящийся, в нашем случае, по адресу 10. 1. 0. То есть, описывают то, через какой физический интерфейс eth0 можно связаться с каждым из мостов. Эти правила определяют правила маршрутизации пакетов, предназначенных для каждого из мостов. Говоря о Kubernetes, я обычно называю эту сеть «сетью подов», так как это — оверлейная сеть, которая позволяет подам, расположенным в разных узлах, взаимодействовать друг с другом. Подобную комбинацию виртуальных сетевых интерфейсов, мостов и правил маршрутизации обычно называют оверлейной сетью. Вот как будет выглядеть предыдущая схема после того, как за дело возьмутся механизмы Kubernetes.

Сеть подов

Kubernetes не использует стандартные мосты Docker. Тут сразу бросается в глаза то, что имена мостов изменены с docker0 на cbr0. Я не готов привести полный список отличий запуска контейнеров Docker в подах от их запуска на обычных компьютерах, но то, о чём мы тут говорим, является одним из важных подобных отличий. То, что мы тут назвали cbr — это сокращение от «custom bridge», то есть, речь идёт о неких особых мостах. 0. Кроме того, надо обратить внимание на то, что адресное пространство, назначенное мостам в этом примере — это 10. 0/14. 0. Вашему кластеру может быть назначен совершенно другой диапазон адресов. Этот адрес взят из одного из наших staging-кластеров, который развёрнут на платформе Google Cloud, так что выше приведён вполне реальный пример сети подов. К сожалению, в настоящий момент нет возможности получить сведения об этих адресах с использованием утилиты kubectl, но, например, если вы пользуетесь GCP, вы можете выполнить команду вида gcloud container clusters describe <cluster> и взглянуть на свойство clusterIpv4Cidr.

Когда под обменивается данными с другим подом, чаще всего это происходит посредством сервисов Kubernetes. В целом же можно отметить, что вам, обычно, не придётся размышлять о том, как именно работает сеть подов. Но сетевые адреса подов появляются в логах. Это — нечто вроде программно определяемых прокси. Например, трафик, покидающий под Kubernetes, привязанный к любому адресу в диапазоне 10. В некоторых ситуациях, в частности, при отладке, вам может понадобиться явным образом задавать правила маршрутизации в сетях подов. 0. 0. Поэтому если вы взаимодействуете с сервисами, находящимися в другой частной сети, имеющей тот же диапазон адресов, вам может понадобиться настроить правила маршрутизации, которые позволят организовать правильную доставку пакетов. 0/8, не обрабатывается по умолчанию с помощью NAT.

Итоги

Сегодня мы поговорили о подах Kubernetes и об особенностях их сетевого взаимодействия. Надеемся, этот материал поможет вам сделать правильные шаги в направлении реализации сложных сценариев взаимодействия подов в сетях Kubernetes.

Вторая часть этого цикла уже переведена. Уважаемые читатели! Эта статья является первым материалом цикла, посвящённого сетям Kubernetes. Просим вас высказаться об этом в комментариях. Мы размышляем о том, нужно ли переводить третью часть.

Теги
Показать больше

Похожие статьи

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»
Закрыть