- PVSM.RU - https://www.pvsm.ru -

Как pod в Kubernetes получает IP-адрес

Прим. перев.: эта статья, написанная SRE-инженером из LinkedIn, в деталях рассказывает о той «внутренней магии» в Kubernetes — точнее, взаимодействии CRI, CNI и kube-apiserver, — что происходит, когда очередному pod'у требуется назначить IP-адрес.

Одно из базовых требований сетевой модели Kubernetes [1] состоит в том, что у каждого pod'а должен быть свой собственный IP-адрес и любой другой pod в кластере должен иметь возможность связаться с ним по этому адресу. Есть множество сетевых «провайдеров» (Flannel, Calico, Canal и т.п.), которые помогают реализовать данную сетевую модель.

Когда я только начинал работать с Kubernetes, мне было не совсем ясно, как именно pod'ы получают свои IP-адреса. Даже с пониманием, как функционируют отдельные компоненты, было сложно представить их совместную работу. Например, я знал, для чего нужны плагины CNI, но не представлял, как именно они вызываются. Поэтому решил написать эту статью, чтобы поделиться знаниями о различных сетевых компонентах и их совместной работе в кластере Kubernetes, которая и позволяет каждому pod'у получить свой уникальный IP-адрес.

Существуют различные способы организации сетевого взаимодействия в Kubernetes — аналогично тому, как и различные варианты исполняемых сред (runtime) для контейнеров. В этой публикации будет использоваться Flannel [2] для организации сети в кластере, а в качестве исполняемой среды — Containerd [3]. Также я исхожу из предположения, что вы знаете, как устроено сетевое взаимодействие между контейнерами, поэтому лишь вкратце затрону его, исключительно для контекста.

Некоторые базовые понятия

Контейнеры и сеть: краткий обзор

В интернете достаточно отличных публикаций, объясняющих, как контейнеры связываются друг с другом по сети. Поэтому проведу лишь общий обзор основных понятий и ограничусь одним подходом, подразумевающим создание Linux-моста и инкапсуляцию пакетов. Подробности опущены, поскольку сама тема сетевого взаимодействия контейнеров заслуживает отдельной статьи. Ссылки на некоторые особенно содержательные и познавательные публикации будут приведены ниже.

Контейнеры на одном хосте

Один из способов организации связи по IP-адресам между контейнерами, работающими на одном и том же хосте, предполагает создание Linux-моста. Для этого в Kubernetes (и Docker) создаются виртуальные устройства veth (virtual ethernet) [4]. Один конец veth-устройства подключается к сетевому пространству имен контейнера, другой — к Linux-мосту [5] в сети хоста.

У всех контейнеров на одном хосте один из концов veth подключен к мосту, через который они могут связываться друг с другом по IP-адресам. У Linux-моста также имеется IP-адрес, и он выступает в качестве шлюза для исходящего (egress) трафика из pod'ов, предназначенного для других узлов.

Как pod в Kubernetes получает IP-адрес - 1

Контейнеры на разных хостах

Инкапсуляция пакетов — один из способов, позволяющий контейнерам на разных узлах связываться друг с другом по IP-адресам. Во Flannel за эту возможность отвечает технология vxlan [6], которая «упаковывает» исходный пакет в пакет UDP и затем отправляет его по назначению.

В кластере Kubernetes Flannel создает устройство vxlan и соответствующим образом дополняет таблицу маршрутов на каждом из узлов. Каждый пакет, предназначенный для контейнера на другом хосте, проходит через устройство vxlan и инкапсулируется в пакет UDP. В пункте назначения вложенный пакет извлекается и перенаправляется на нужный pod.

Как pod в Kubernetes получает IP-адрес - 2
Примечание: Это лишь один из способов организации сетевого взаимодействия между контейнерами.

Что такое CRI?

CRI (Container Runtime Interface) [7] — это плагин, позволяющий kubelet'у использовать разные исполняемые среды контейнеров. API CRI встроен в различные исполняемые среды, поэтому пользователи могут выбирать runtime по своему усмотрению.

Что такое CNI?

Проект CNI [8] представляет собой спецификацию [9] для организации универсального сетевого решения для Linux-контейнеров. Кроме того, он включает в себя плагины [10], отвечающие за различные функции при настройке сети pod'а. Плагин CNI — это исполняемый файл, соответствующи спецификации (некоторые плагины мы обсудим ниже).

Выделение подсетей узлам для назначения IP-адресов pod'ам

Поскольку каждый pod кластера должен иметь IP-адрес, важно убедиться в том, чтобы этот адрес был уникальным. Это достигается путем выделения каждому узлу уникальной подсети, из которой затем pod'ам на этом узле назначаются IP-адреса.

Контроллер IPAM узла

Когда nodeipam передается в качестве параметра флага --controllers kube-controller-manager'а [11], он каждому узлу выделяет отдельную подсеть (podCIDR) из CIDR кластера (т.е. диапазона IP-адресов для сети кластера). Поскольку эти podCIDR'ы не пересекаются, становится возможным каждому pod'у выделить уникальный IP-адрес.

Узлу Kubernetes присваивается podCIDR в момент его начальной регистрации в кластере. Чтобы изменить podCIDR у узлов, необходимо их дерегистрировать и затем повторно зарегистрировать, в промежутке внеся соответствующие изменения в конфигурацию управляющего слоя Kubernetes. Вывести podCIDR узла можно с помощью следующей команды:

$ kubectl get no <nodeName> -o json | jq '.spec.podCIDR'
10.244.0.0/24

Kubelet, среда запуска контейнера и плагины CNI: как это все работает

Планирование pod'а на узел связано с выполнением множества подготовительных действий. В этом разделе я сконцентрируюсь только на тех из них, которые непосредственно связаны с настройкой сети pod'а.

Планирование pod'а на некий узел запускает следующую цепочку событий:

Как pod в Kubernetes получает IP-адрес - 3

Справка: Архитектура CRI-плагинов Containerd [12].

Взаимодействие среды запуска контейнеров и плагинов CNI

У каждого сетевого провайдера имеется свой плагин CNI. Runtime контейнера запускает его, чтобы сконфигурировать сеть для pod'a в процессе его запуска. В случае containerd запуском CNI-плагина занимается плагин Containerd CRI [13].

При этом у каждого провайдера есть свой агент. Он устанавливается во все узлы Kubernetes и отвечает за сетевую настройку pod'ов. Этот агент идет либо в комплекте с конфигом CNI, либо самостоятельно создает его на узле. Конфиг помогает CRI-плагину установить, какой плагин CNI вызывать.

Местонахождение конфига CNI можно настроить; по умолчанию он лежит в /etc/cni/net.d/<config-file>. Администраторы кластера также отвечают за установку плагинов CNI на каждый узел кластера. Их местонахождение также настраивается; директория по умолчанию — /opt/cni/bin.

При использовании containerd пути для конфига и бинарников плагина можно задать в разделе [plugins.«io.containerd.grpc.v1.cri».cni] в файле конфигурации containerd [14].

Поскольку мы используем Flannel в качестве сетевого провайдера, давайте немного поговорим о его настройке:

  • Flanneld (демон Flannel'а) обычно устанавливается в кластер как DaemonSet с install-cni в качестве init-контейнера [15].
  • Install-cni создает файл конфигурации CNI [16] (/etc/cni/net.d/10-flannel.conflist) на каждом узле.
  • Flanneld создает устройство vxlan, извлекает сетевые метаданные с API-сервера и следит за обновлениями pod'ов. По мере их создания он распространяет маршруты для всех pod'ов по всему кластеру.
  • Эти маршруты и позволяют pod'ам связываться друг с другом по IP-адресам.

Для получения более подробной информации о работе Flannel рекомендую воспользоваться ссылками в конце статьи.

Вот схема взаимодействия между плагином Containerd CRI и плагинами CNI:

Как pod в Kubernetes получает IP-адрес - 4

Как видно выше, kubelet вызывает плагин Containerd CRI, чтобы создать pod, а тот уже вызывает плагин CNI для настройки сети pod'а. При этом CNI-плагин сетевого провайдера вызывает другие базовые плагины CNI для настройки различных аспектов сети.

Взаимодействие между CNI-плагинами

Существуют различные плагины CNI, задача которых — помочь настроить сетевое взаимодействие между контейнерами на хосте. В этой статье речь пойдет о трех из них.

CNI-плагин Flannel

При использовании Flannel в качестве сетевого провайдера компонент Containerd CRI вызывает CNI-плагин Flannel [17], используя конфигурационный файл CNI /etc/cni/net.d/10-flannel.conflist.

$ cat /etc/cni/net.d/10-flannel.conflist
{
  "name": "cni0",
  "plugins": [
    {
      "type": "flannel",
      "delegate": {
         "ipMasq": false,
        "hairpinMode": true,
        "isDefaultGateway": true
      }
    }
  ]
}

CNI-плагин Flannel работает совместно с Flanneld. Во время запуска Flanneld извлекает podCIDR и другие связанные с сетью подробности из API-сервера и сохраняет их в файл /run/flannel/subnet.env.

FLANNEL_NETWORK=10.244.0.0/16 
FLANNEL_SUBNET=10.244.0.1/24
FLANNEL_MTU=1450 
FLANNEL_IPMASQ=false

CNI-плагин Flannel использует данные из /run/flannel/subnet.env для настройки и вызова CNI-плагина моста (bridge).

CNI-плагин Bridge

Этот плагин вызывается со следующей конфигурацией:

{
  "name": "cni0",
  "type": "bridge",
  "mtu": 1450,
  "ipMasq": false,
  "isGateway": true,
  "ipam": {
    "type": "host-local",
    "subnet": "10.244.0.0/24"
  }
}

При первом вызове он создает Linux-мост с «name»: «cni0», что указывается в конфиге. Затем для каждого pod'а создается пара veth. Один ее конец подключается к сетевому пространству имен контейнера, другой входит в Linux-мост в сети хоста. CNI-плагин Bridge [18] подключает все контейнеры хоста к Linux-мосту в сети хоста.

Закончив с настройкой пары veth, плагин Bridge вызывает локальный для хоста (host-local) CNI-плагин IPAM. Тип IPAM-плагина можно настроить в конфиге CNI, который плагин CRI использует для вызова CNI-плагина Flannel.

Локальные для хоста IPAM-плагины CNI

Bridge CNI вызывает хост-локальный IPAM-плагин CNI [19] со следующей конфигурацией:

{
  "name": "cni0",
  "ipam": {
    "type": "host-local",
    "subnet": "10.244.0.0/24",
    "dataDir": "/var/lib/cni/networks"
  }
}

Host-local IPAM-плагин (IP Address Management — управление IP-адресами) возвращает IP-адрес для контейнера из подсети и сохраняет выделенный IP на хосте в директории, указанной в разделе dataDir/var/lib/cni/networks/<network-name=cni0>/<ip>. В этом файле содержится ID контейнера, которому присвоен данный IP-адрес.

При вызове host-local IPAM-плагина он возвращает следующие данные:

{
  "ip4": {
    "ip": "10.244.4.2",
    "gateway": "10.244.4.3"
  },
  "dns": {}
}

Резюме

Kube-controller-manager каждому узлу присваивает podCIDR. Pod'ы каждого узла получают IP-адреса из пространства адресов в выделенном диапазоне podCIDR. Поскольку podCIDR'ы узлов не пересекаются, все pod'ы получают уникальные IP-адреса.

Администратор кластера Kubernetes настраивает и устанавливает kubelet, среду запуска контейнеров, агента сетевого провайдера и копирует плагины CNI на каждый узел. Во время старта агент сетевого провайдера генерирует конфиг CNI. Когда pod планируется на узел, kubelet вызывает CRI-плагин для его создания. Далее, если используется containerd, плагин Containerd CRI вызывает CNI-плагин, указанный в конфиге CNI, для настройки сети pod'а. В результате pod получает IP-адрес.

Мне потребовалось некоторое время, чтобы разобраться во всех тонкостях и нюансах всех этих взаимодействий. Надеюсь, полученный опыт поможет и вам лучше понять, как работает Kubernetes. Если я в чем-то ошибаюсь, пожалуйста, свяжитесь со мной в Twitter [20] или по адресу hello@ronaknathani.com [21]. Не стесняйтесь обращаться, если захотите обсудить аспекты этой статьи или что-нибудь другое. Я с удовольствием пообщаюсь с вами!

Ссылки

Контейнеры и сеть

Как работает Flannel

CRI и CNI

P.S. от переводчика

Читайте также в нашем блоге:

Автор: Василий Марютенков

Источник [30]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/sistemnoe-administrirovanie/357471

Ссылки в тексте:

[1] сетевой модели Kubernetes: https://kubernetes.io/docs/concepts/cluster-administration/networking/#the-kubernetes-network-model

[2] Flannel: https://github.com/coreos/flannel

[3] Containerd: https://github.com/containerd/containerd

[4] veth (virtual ethernet): https://man7.org/linux/man-pages/man4/veth.4.html

[5] Linux-мосту: https://wiki.archlinux.org/index.php/Network_bridge

[6] vxlan: https://vincent.bernat.ch/en/blog/2017-vxlan-linux

[7] CRI (Container Runtime Interface): https://github.com/kubernetes/cri-api

[8] Проект CNI: https://github.com/containernetworking/cni

[9] спецификацию: https://github.com/containernetworking/cni/blob/master/SPEC.md

[10] плагины: https://github.com/containernetworking/plugins

[11] kube-controller-manager'а: https://kubernetes.io/docs/reference/command-line-tools-reference/kube-controller-manager/

[12] Архитектура CRI-плагинов Containerd: https://github.com/containerd/cri/blob/v1.11.1/docs/architecture.md

[13] Containerd CRI: https://github.com/containerd/cri

[14] файле конфигурации containerd: https://github.com/containerd/cri/blob/master/docs/config.md

[15] init-контейнера: https://github.com/coreos/flannel/blob/master/Documentation/kube-flannel.yml#L172

[16] файл конфигурации CNI: https://gist.github.com/ronaknnathani/957a56210bd4fbd8e11120273c6b4ede

[17] CNI-плагин Flannel: https://github.com/containernetworking/plugins/tree/master/plugins/meta/flannel

[18] CNI-плагин Bridge: https://github.com/containernetworking/plugins/tree/master/plugins/main/bridge

[19] хост-локальный IPAM-плагин CNI: https://github.com/containernetworking/plugins/tree/master/plugins/ipam/host-local

[20] Twitter: https://twitter.com/RonakNathani

[21] hello@ronaknathani.com: mailto:hello@ronaknathani.com

[22] A container networking overview: https://jvns.ca/blog/2016/12/22/container-networking/

[23] Demystifying container networking: https://blog.mbrt.dev/2017-10-01-demystifying-container-networking/

[24] Flannel Networking Demystify: https://msazure.club/flannel-networking-demystify/

[25] Kubernetes With Flannel — Understanding The Networking: https://medium.com/@anilkreddyr/kubernetes-with-flannel-understanding-the-networking-part-2-78b53e5364c7

[26] Calico для сети в Kubernetes: знакомство и немного из опыта: https://habr.com/ru/company/flant/blog/485716/

[27] части 1 и 2 (сетевая модель, оверлейные сети): https://habr.com/ru/company/flant/blog/346304/

[28] часть 3 (сервисы и обработка трафика): https://habr.com/ru/company/flant/blog/433382/

[29] Container Networking Interface (CNI) — сетевой интерфейс и стандарт для Linux-контейнеров: https://habr.com/ru/company/flant/blog/329830/

[30] Источник: https://habr.com/ru/post/521406/?utm_source=habrahabr&utm_medium=rss&utm_campaign=521406