Flannel Kubernetes the hard way v2

Материал из noname.com.ua
Перейти к навигацииПерейти к поиску


Flannel

Flannel - один из способов организации оверлейной сети

Зачем это нужно

  • В частных случаях когда сеть полностью контролируема и можно назначать любые маршруты и адреса на всех устройствах - не нужно строить никаких оверлейных сетей
  • Если сеть не контролируется (например в арендованном датацентре или облаке, и не возможно создание маршрутов между worker-nodes то это один из множества возможных способов обеспечить оверлейную сеть

Как это устроено

"под капотом" используется VxLAN

Подробнее про VxLAN

"VxLAN это не только тунель но и свитч" =) точнее сказать распределенный свитч


VxLAN инкапсулирует кадры Ethernet уровня 2 в пакеты UDP уровня 3, что означает, что виртуальные подсети уровня 2 могут охватывать базовые сети уровня.
Другими словами, это дает возможность строить "продолжение" 2 уровня поверх третьего,
например соединить 2 географически разнесенных сегмента сети, между которыми нет физического
соединения (а только логическое, через интернет или другую сеть с маршрутизацией а не коммутацией)
так, что бы с точки зрения подключенных устройств это выглядело бы как-будто они включены в
один сегмент (например в один свитч, или для бытового уровня - в один домашний роутер).

VxLAN помешает кадры etherneth внутрь пакета UDP который в свою очередь инкапсулируется (помещается внутрь) в пакет IP.
Результирующий пакет IP может быть передан далее по IP-сетям, в том числе и через интернет. На стороне получателя устройство с
настроенной поддержкой VxLAN производит обратную процедуру - деинкапсуляцию

Впрочем, процесс инкапсуляция/деинкапсуляция применяется весьма широко и ничего нового в нем нет.

Терминология

VNI

Идентификатор сети VxLAN (VNI) используется для сегментации каждой подсети уровня 2 аналогично традиционным идентификаторам VLAN.

VTEP

Конечная точка туннеля VxLAN (VTEP, VxLAN Tunnel End Point) — это устройство с поддержкой VXLAN, которое инкапсулирует и деинкапсулирует пакеты.
Это может быть как программная реализация (Linux или OpenVSwitch в котором есть свой механизм для работы с VxLAN) или аппаратная (Коммутаторы с поддержкой VxLAN).

Схема лабы (несколько упрощенная)

На схеме не изображены мастер-ноды так как они в данном случае не участвуют в процессе передачи траффика.

  • Worker1 - 192.168.122.88 (физическая сетевая карта)
  • Worker3 - 192.168.122.114 (физическая сетевая карта)

Адреса остальных нод на схеме значения не имеют

Для POD выделено адресное пространство (первый адрес зарезервирован за шлюзом на интерфейс cni0)

  • Worker1: 10.244.1.0/24
  • Worker2: 10.244.2.0/24 (не участвует)
  • Worker3: 10.244.3.0/24

Схема

+-------------------------+    +-------------------------+    +-------------------------+                 +-------------------------+
|  node: worker1          |    |  node: worker2          |    |  node: worker3          |                 |  node: workerN          |
|                         |    |                         |    |                         |                 |                         |
|  +------------+         |    |  +------------+         |    |  +------------+         |                 |  +------------+         |
|  | POD 1      |         |    |  | POD 2      |         |    |  | POD 3      |         |                 |  | POD N      |         |
|  |            |         |    |  |            |         |    |  |            |         |                 |  |            |         |
|  |            |         |    |  |            |         |    |  |            |         |                 |  |            |         |
|  +--eth0------+         |    |  +--eth0------+         |    |  +--eth0------+         |                 |  +--eth0------+         |
|      |                  |    |      |                  |    |      |                  |                 |      |                  |
|      |                  |    |      |                  |    |      |                  |                 |      |                  |
|  +--vethX---------+     |    |  +--vethX---------+     |    |  +--vethX---------+     |                 |  +--vethX---------+     |
|  |                |     |    |  |                |     |    |  |                |     |      .....      |  |                |     |
|  |  cni0 (bridge) |     |    |  |  cni0 (bridge) |     |    |  |  cni0 (bridge) |     |                 |  |  cni0 (bridge) |     |
|  |  10.244.1.1/24 |     |    |  |  10.244.2.1/24 |     |    |  | 10.244.3.1/24  |     |                 |  | 10.244.X.1/24  |     |
|  +----------------+     |    |  +----------------+     |    |  +----------------+     |                 |  +----------------+     |
|                         |    |                         |    |                         |                 |                         |
|                         |    |                         |    |                         |                 |                         |
|  +-------------------+  |    |  +-------------------+  |    |  +-------------------+  |                 |  +-------------------+  |
|  | flannel.1 (VxLan) |  |    |  | flannel.1 (VxLan) |  |    |  | flannel.1 (VxLan) |  |                 |  | flannel.1 (VxLan) |  |
|  +------+------------+  |    |  +------+------------+  |    |  +------+------------+  |                 |  +-------+-----------+  |
|         |               |    |         |               |    |         |               |                 |          |              |
|         |               |    |         |               |    |         |               |                 |          |              |
|         +------------------------------+------------------------------+-------------------   .....    -------------+              |
|                         |    |                         |    |                         |                 |                         |
|                         |    |                         |    |                         |                 |                         |
+----- enp1s0--------------+   +----- enp1s0-------------+    +----- enp1s0-------------+                 +----- enp1s0-------------+
    192.168.0.88                    192.168.0.XX                 192.168.122.114                             192.168.122.YY
         |                              |                              |                                           |
    +----+------------------------------+------------------------------+--------------------             ----------+----+
    | L2 connected network (switch or virtual switch)                                          .....                    |
    +------------------------------------------------------------------+--------------------             ----------+----+

veth/cni0

Для каждой ноды (это делается при создании POD, точно так же делает docker)

  • Для простоты изображен только один POD (их может быть любое количество)
  • При создании POD создается "виртуальный патч-корд", veth, это особый тип виртуального etherneth - два интерфейса соединенных между собой
  • Один из пары виртуальных интерфейсов veth помещается в сетевое пространство имен POD (network namespace, так жи иногда используется термин vrf)
  • Внутри POD он именуется как eth0 (именовать интерфейсы можно любым способом, уникальным имя должно быть только в пределах namespace)
  • Таким образом, так как виртуальный патч-корд ведет из POD в сетевую подсистему хост-машины
  • Второй конец випртуального патч-корда включен в бридж cni0


Flannel

Интерфейс flannel это VxLAN, проверить это можно командой:

ip -d link show dev flannel.1

В примере показан интерфейс для ноды worker3

flannel.1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UNKNOWN mode DEFAULT group default 
    link/ether 86:df:93:cc:8e:62 brd ff:ff:ff:ff:ff:ff promiscuity 0 
    vxlan id 1 local 192.168.122.114 dev enp1s0 srcport 0 0 dstport 8472 nolearning ttl inherit ageing 300 udpcsum noudp6zerocsumtx noudp6zerocsumrx addrgenmode eui64 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535 

Тут нужно обратить внимание на следующие настройки:

  • vxlan id 1 VNI=1 (идентификатор сети, нужен если нужно более больше VxLAN)
  • local 192.168.122.114 dev enp1s0 Адрес и интерфейс которые используются для отправки пакетов. Так как тут не указан параметр remote то он будет взят из таблицы "коммутации" (условно)
  • srcport 0 0 dstport 8472 Порт куда отправлять пакеты UDP
  • nolearning Адреса в таблицу "коммутации" добавляются в ручном режиме (в случае с Flannel - как раз процессом flanneld) (???? проверить)

Остальное менее значимо : ttl inherit ageing 300 udpcsum noudp6zerocsumtx noudp6zerocsumrx addrgenmode eui64 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535

Для простоты восприятия VxLAN можно думать о нем как о виртуальном свитче распределённом по нескольким физическим нодам, при ээтом в таблице "коммутации" этого "свитча" вместо портов, куда отправлять пакеты для известных адресов, содержаться IP адреса нод
Таблица заполняется статически, таким образом эта схема похожа на ситуацию когда на коммутаторе выключен mac-learning и для каждого мака рукам (или сторонним ПО, но не средствами коммутатора) создается запись в таблице коммутации.
Для сравнения VxLAN: запись следует читать так: для отправки кадра мак адресу a2:99:54:8b:35:bd в UDP пакете указать получателя dst 192.168.122.88 и порт dstport 8472 (порт взят из ip -d dev show ..

a2:99:54:8b:35:bd dst 192.168.122.88 self permanent

Для сравнения, на физическом коммутаторе запись читать так: для отправки кадра на мак 00cc.3444.b074 в Vlan 123 использовать физический порт TenGigabitEthernet1/50

#show mac address-table
Unicast Entries
 vlan     mac address     type        protocols               port
---------+---------------+--------+---------------------+-------------------------
123      00cc.3444.b074   dynamic ip,ipx,assigned,other TenGigabitEthernet1/50

В целом аналогия достаточно близкая.

Разбор прохождения пакетов между двумя POD запущенными на разных нодах

Далее разберем работу Flannel на примере прохождения пакетов из одного POD запущенного на worker3 на другой запущенный на ноде worker1 Для простоты буду называть

  • pod запущенный на ноде worker3 - pod3
  • pod запущенный на ноде worker1 - pod1


Маршрут от pod3 к pod2

Проверяем как идет маршрутизация из pod3 к адресу pod1: 10.244.1.34

# traceroute 10.244.1.34
traceroute to 10.244.1.34 (10.244.1.34), 30 hops max, 60 byte packets
 1  10.244.3.1 (10.244.3.1)  0.156 ms  0.048 ms  0.040 ms
 2  10.244.1.0 (10.244.1.0)  2.451 ms  2.340 ms  2.239 ms
 3  10.244.1.34 (10.244.1.34)  2.143 ms  2.051 ms  1.954 ms

Из этого маршрута видно три "маршрутизатора" 1 - это шлюз для POD (согласно схеме это обычно интерфейс cni0

Таблица маршрутизации pod3

Проверяем таблицу маршрутизации внутри pod3

# ip route show
default via 10.244.3.1 dev eth0 
10.244.0.0/16 via 10.244.3.1 dev eth0 
10.244.3.0/24 dev eth0 proto kernel scope link src 10.244.3.254 

Из этого видно что первый хоп (первый адрес в трейсе) это адрес шлюза для pod3 (10.244.3.1), это адрес интерфейса cni0 на ноде worker3, на которой и запущен pod


Шлюз для pod3

В этом можно убедиться, запустив ifconfigна ноде worker3 за пределами контейнера

root@worker3:/home/ubuntu# ifconfig cni0
cni0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1450
        inet 10.244.3.1  netmask 255.255.255.0  broadcast 10.244.3.255
        inet6 fe80::f8c6:3aff:fe2b:8a  prefixlen 64  scopeid 0x20<link>
        ether fa:c6:3a:2b:00:8a  txqueuelen 1000  (Ethernet)
        RX packets 112  bytes 6220 (6.2 KB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 76  bytes 6164 (6.1 KB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

После получения пакета на интерфейсе cni0 он будет передан дальше согласно имеющимся записям в таблице маршрутизации.

Таблица маршрутизации на worker3

После получения пакета нодой worker3 далее он следует согласно ее таблице маршрутизации:

ip route show
default via 192.168.122.1 dev enp1s0 proto dhcp src 192.168.122.114 metric 100 
10.244.0.0/24 via 10.244.0.0 dev flannel.1 onlink 
10.244.1.0/24 via 10.244.1.0 dev flannel.1 onlink 
10.244.2.0/24 via 10.244.2.0 dev flannel.1 onlink 
10.244.3.0/24 dev cni0 proto kernel scope link src 10.244.3.1 
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 linkdown 
192.168.122.0/24 dev enp1s0 proto kernel scope link src 192.168.122.114 
192.168.122.1 dev enp1s0 proto dhcp scope link src 192.168.122.114 metric 100 

Здесь видно что пакет попадает под строку 10.244.1.0/24 via 10.244.1.0
Тот же результат можно получить воспользовавшись командой ip route get <IP адрес>

Маршруты с флагом onlink

Найденный в таблице маршрутизации маршрут (с некоторыми особенностями)

10.244.1.0/24 via 10.244.1.0 dev flannel.1 onlink

Внимание нужно обратить на то что у маршрута есть флаг onlink
Вот цитата из руководства iproute2 https://man7.org/linux/man-pages/man8/ip-route.8.html

onlink pretend that the nexthop is directly attached to
this link, even if it does not match any interface prefix.


Это означает что для маршрутов с таким флагом onlinkне обязательно иметь адрес назначенный на интерфейсе (в примере это flannel.1)
который находился бы в одной сети с шлюзом, через которые отправлять пакеты

Получение ARP-записи для адреса 10.244.1.0

Очевидно, что для того что бы отправить пакет далее на шлюз с адресом 10.244.1.0
необходимо иметь запись в таблице arp.
Проверить такое наличие можно командой arp на ноде worker3 (за пределами контейнеров)

# arp -n
Address                  HWtype  HWaddress           Flags Mask            Iface
10.244.1.0               ether   a2:99:54:8b:35:bd   CM                    flannel.1

Далее пакет отправляется через устройство flannel.1, для этого есть все необходимые данные:

  • мак-адрес получателя (что бы сформировать etherneth-кадр)
  • физический или виртуальный интерфейс через который отправлять сформированный кадр

Отправка пакета через VxLAN интрерфейс


Так как устройство flannel.1 является VxLAN то далее работает в чем-то похоже на виртуальный коммутатор,
таблицу коммутации которого можно просмотреть (оставлена только значимая запись для нужного МАК-адреса, остальные пропущены):

bridge fdb show dev flannel.1
...
a2:99:54:8b:35:bd dst 192.168.122.88 self permanent
...

Тут есть некоторые отличия от обычной таблицы коммутации

  • "обычные" классические свитчи ищут в таблице какой мак был изучен на каком порту и в каком VLAN
  • тут таблицу следует читать так: что бы отправить кадр на мак получателя (a2:99:54:8b:35:bd) нужно в IP пакете, в который будем инкапсулировать этот кадр, в качестве получателя указать адрес 192.168.122.88
  • 192.168.122.88 - это адрес ноды worker1 на которой и расположен нужный POD


Повторю, что записи таблице "коммутации" в реализации Flannel возникают не магическим образом, а их добавляет демон flanneld, который в свою очередь, имея доступ к API kube-apiserver имеет информацию для заполнения таблички, а именно

  • MAC-адреса POD-ов запущенных на ноде
  • IP-адрес ноды (kubelet)

Соответственно таблица коммутации на каждой ноде выглядит так (с допущением что на каждой ноде запущен только один POD)

[Мак адрес POD1 на ноде 1]  dst [IP адрес ноды 1]
[Мак адрес POD2 на ноде 2]  dst [IP адрес ноды 2]
[Мак адрес POD2 на ноде 3]  dst [IP адрес ноды 3]
...
[Мак адрес POD2 на ноде N]  dst [IP адрес ноды N]

Важное замечание:

  • Такой способ заполнения таблицы не единственно возможный, есть и другие реализации
  • Ручное удаление записей из таблицы приводит к тому что данные перестают передаваться (что совершенно ожидаемо)
  • Простейший способ восстановить связь после ручного удаления записей - это рестарт пода flanneldна ноде, где были удалены записи. При старте

POD актуализирует таблицу "коммутации" интерфейса flannel.1


===Немного детальнее про формат VxLAN Выше есть некоторое упрощение - инкапсуляция проходит в несколько этапов

  • фрейм etherneth помещается в UPD
  • UDP помещается в IP

Формат пакета VxLAN (https://www.routexp.com/2019/02/vxlan-encapsulation-and-packet-format.html)
VxlanFormat.png

Передача между нодами

Далее пакет (с инкапсулированным фреймом) должен быть отправлен на адрес 192.168.122.88 (как было выяснено из таблицы "коммутации" интерфейса flannel.1)


Для этого снова обращаемся к таблице маршрутизации хоста worker3:

ip route show
default via 192.168.122.1 dev enp1s0 proto dhcp src 192.168.122.114 metric 100 
10.244.0.0/24 via 10.244.0.0 dev flannel.1 onlink 
10.244.1.0/24 via 10.244.1.0 dev flannel.1 onlink 
10.244.2.0/24 via 10.244.2.0 dev flannel.1 onlink 
10.244.3.0/24 dev cni0 proto kernel scope link src 10.244.3.1 
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 linkdown 
192.168.122.0/24 dev enp1s0 proto kernel scope link src 192.168.122.114 
192.168.122.1 dev enp1s0 proto dhcp scope link src 192.168.122.114 metric 100

Здесь видно что сеть 192.168.122.0/24, а значит и адрес 192.168.122.88 доступны через интерфейс enp1s0
Это видно из строки 192.168.122.0/24 dev enp1s0 proto kernel scope link src 192.168.122.114 или воспользоваться командой ip route get ...


Далее происходит классическая отправка пакета через ethernet (без подробностей)

  • Выяснить мак получателя (arp)
  • сформировать кадр, инкапсулировав в него пакет
  • отправить через интерфейс enp1s0

Получение пакета на ноде worker1

Пакет попадает на ноду worker1 так как в сформированном IP , UDP пакет деинкапсулируется и результирующий кадр доставляется на интерфейс flannel.1

ifconfig flannel.1
flannel.1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1450
        inet 10.244.1.0  netmask 255.255.255.255  broadcast 0.0.0.0
        inet6 fe80::a099:54ff:fe8b:35bd  prefixlen 64  scopeid 0x20<link>
        ether a2:99:54:8b:35:bd  txqueuelen 0  (Ethernet)
        RX packets 38  bytes 2424 (2.4 KB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 24  bytes 2088 (2.0 KB)
        TX errors 0  dropped 16 overruns 0  carrier 0  collisions 0

Далее оригинальный пакет извлекается из кадра etherneth и маршрутизируется согласно таблице маршрутизации на ноде worker1

Маршрутизация на ноде worker1

ip route show
default via 192.168.122.1 dev enp1s0 proto dhcp src 192.168.122.88 metric 100 
10.244.0.0/24 via 10.244.0.0 dev flannel.1 onlink 
10.244.1.0/24 dev cni0 proto kernel scope link src 10.244.1.1 
10.244.2.0/24 via 10.244.2.0 dev flannel.1 onlink 
10.244.3.0/24 via 10.244.3.0 dev flannel.1 onlink 
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 linkdown 
192.168.122.0/24 dev enp1s0 proto kernel scope link src 192.168.122.88 
192.168.122.1 dev enp1s0 proto dhcp scope link src 192.168.122.88 metric 100 

Тут видно что сеть 10.244.1.0/24 и в том числе и адрес назначения 10.244.1.34 доступны через интерфейс cni0
То же самое можно подтвердить командой:

ip route  get 10.244.1.34
10.244.1.34 dev cni0 src 10.244.1.1 uid 0 
    cache 

Зная интерфейс, далее пакет отправляется через него так как через обычный ethernet

  • Выясняем мак получателя с помощью arp
  • Формируем и отправляем кадр etherneth конечному получателю

Результат

Оригинальный пакет через оверлейную сеть доставлен получателю.

Что дальше

Calico ?