Flannel Kubernetes the hard way v2: различия между версиями
Sirmax (обсуждение | вклад) |
Sirmax (обсуждение | вклад) |
||
(не показаны 74 промежуточные версии этого же участника) | |||
Строка 2: | Строка 2: | ||
[[Категория:Networking]] |
[[Категория:Networking]] |
||
[[Категория:Linux]] |
[[Категория:Linux]] |
||
+ | [[Категория:Flannel]] |
||
[[Категория:Kubernetes the hard way v2]] |
[[Категория:Kubernetes the hard way v2]] |
||
+ | [[Категория:VxLAN]] |
||
− | |||
=Flannel= |
=Flannel= |
||
Строка 12: | Строка 13: | ||
=Как это устроено= |
=Как это устроено= |
||
− | "под капотом" используется VxLAN |
+ | "под капотом" используется <code>VxLAN</code> |
* https://community.fs.com/ru/blog/what-is-evpn-vxlan.html |
* https://community.fs.com/ru/blog/what-is-evpn-vxlan.html |
||
=Подробнее про VxLAN= |
=Подробнее про VxLAN= |
||
− | "VxLAN это не только тунель но и свитч" =) точнее сказать распределенный свитч |
+ | "<code>VxLAN</code> это не только тунель но и свитч" =) точнее сказать распределенный свитч |
<BR> |
<BR> |
||
− | + | <code>VxLAN</code> инкапсулирует кадры <code>Ethernet</code> уровня 2 в пакеты UDP уровня 4, что означает, что виртуальные подсети уровня 2 могут охватывать базовые сети уровня.<BR> |
|
− | Другими словами, это дает возможность строить "продолжение" 2 уровня поверх третьего, <BR> |
+ | Другими словами, это дает возможность строить "продолжение" сети 2 уровня поверх сети третьего уровня, <BR> |
например соединить 2 географически разнесенных сегмента сети, между которыми нет физического <BR> |
например соединить 2 географически разнесенных сегмента сети, между которыми нет физического <BR> |
||
соединения (а только логическое, через интернет или другую сеть с маршрутизацией а не коммутацией) <BR> |
соединения (а только логическое, через интернет или другую сеть с маршрутизацией а не коммутацией) <BR> |
||
Строка 27: | Строка 28: | ||
<BR> |
<BR> |
||
<BR> |
<BR> |
||
− | VxLAN помешает кадры |
+ | <code>VxLAN</code> помешает кадры <code>ethernet</code> внутрь пакета UDP который в свою очередь инкапсулируется (помещается внутрь) в пакет IP. <BR> |
Результирующий пакет IP может быть передан далее по IP-сетям, в том числе и через интернет. На стороне получателя устройство с <BR> |
Результирующий пакет IP может быть передан далее по IP-сетям, в том числе и через интернет. На стороне получателя устройство с <BR> |
||
настроенной поддержкой VxLAN производит обратную процедуру - деинкапсуляцию<BR> |
настроенной поддержкой VxLAN производит обратную процедуру - деинкапсуляцию<BR> |
||
Строка 37: | Строка 38: | ||
===VNI=== |
===VNI=== |
||
− | Идентификатор сети |
+ | Идентификатор сети <code>VxLAN</code> (VNI) используется для сегментации каждой подсети уровня 2 аналогично традиционным идентификаторам VLAN. |
===VTEP=== |
===VTEP=== |
||
− | Конечная точка туннеля |
+ | Конечная точка туннеля <code>VxLAN</code> (VTEP, VxLAN Tunnel End Point) — это устройство с поддержкой VXLAN, которое инкапсулирует и деинкапсулирует пакеты. <BR> |
− | Это может быть как программная реализация (Linux или OpenVSwitch в котором есть свой механизм для работы с VxLAN) или аппаратная (Коммутаторы с поддержкой VxLAN). |
+ | Это может быть как программная реализация (Linux или OpenVSwitch в котором есть свой механизм для работы с <code>VxLAN</code>) или аппаратная (Коммутаторы с поддержкой <code>VxLAN</code>). |
<BR> |
<BR> |
||
+ | =Схема лабы (несколько упрощенная)= |
||
− | =Разбор прохождения пакетов между двумя POD запущенными на разных нодах= |
||
− | Далее разберем работу Flannel на примере прохождения пакетов из одного POD запущенного на worker3 на другой запущенный на ноде worker1 |
||
− | Для простоты буду называть |
||
− | * pod запущенный на ноде worker3 - '''pod3''' |
||
− | * pod запущенный на ноде worker1 - '''pod1''' |
||
− | <BR> |
||
− | ==Схема лабы (несколько упрощенная)== |
||
На схеме не изображены мастер-ноды так как они в данном случае не участвуют в процессе передачи траффика. |
На схеме не изображены мастер-ноды так как они в данном случае не участвуют в процессе передачи траффика. |
||
− | * Worker1 - 192.168.122.88 (физическая сетевая карта) |
+ | * '''Worker1''' - <code>192.168.122.88 </code> (физическая сетевая карта) |
− | * Worker3 - 192.168.122.114 (физическая сетевая карта) |
+ | * '''Worker3''' - <code>192.168.122.114</code> (физическая сетевая карта) |
Адреса остальных нод на схеме значения не имеют |
Адреса остальных нод на схеме значения не имеют |
||
+ | Для POD выделено адресное пространство (первый адрес зарезервирован за шлюзом на интерфейс cni0) |
||
+ | * '''Worker1''': <code>10.244.1.0/24</code> |
||
+ | * '''Worker2''': <code>10.244.2.0/24</code> (не участвует) |
||
+ | * '''Worker3''': <code>10.244.3.0/24</code> |
||
+ | ==Схема== |
||
− | ==='''veth/cni0'''=== |
||
− | Для каждой ноды (это делается при создании POD, точно так же делает <code>docker</code>) |
||
− | * Для простоты изображен только один POD (их может быть любое количество) |
||
− | * При создании POD создается "виртуальный патч-корд", <code>veth</code>, это особый тип виртуального etherneth - два интерфейса соединенных между собой |
||
− | * Один из пары виртуальных интерфейсов <code>veth</code> помещается в сетевое пространство имен POD (network namespace, так жи иногда используется термин vrf) |
||
− | * Внутри POD он именуется как eth0 (именовать интерфейсы можно любым способом, уникальным имя должно быть только в пределах namespace) |
||
− | * Таким образом, так как виртуальный патч-корд ведет из POD в сетевую подсистему хост-машины |
||
− | * Второй конец випртуального патч-корда включен в бридж <code>cni0</code> |
||
− | |||
− | |||
− | ==='''Flannel'''=== |
||
− | Интерфейс flannel это <code>VxLAN</code>, проверить это можно командой: |
||
− | <PRE> |
||
− | ip -d link show dev flannel.1 |
||
− | </PRE> |
||
− | <PRE> |
||
− | 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 |
||
− | </PRE> |
||
− | Тут нужно обратить внимание на следующие настройки: |
||
− | * <code>vxlan id 1 </code> |
||
− | * <code>local 192.168.122.114 dev enp1s0 </code> |
||
− | * <code>srcport 0 0 dstport 8472 </code> |
||
− | * <code>nolearning </code> |
||
− | Остальное ttl inherit ageing 300 udpcsum noudp6zerocsumtx noudp6zerocsumrx addrgenmode eui64 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535 |
||
− | |||
− | |||
− | |||
<PRE> |
<PRE> |
||
+-------------------------+ +-------------------------+ +-------------------------+ +-------------------------+ |
+-------------------------+ +-------------------------+ +-------------------------+ +-------------------------+ |
||
Строка 101: | Строка 72: | ||
| | | | | | | | | | | | ..... | | | | |
| | | | | | | | | | | | ..... | | | | |
||
| | cni0 (bridge) | | | | cni0 (bridge) | | | | cni0 (bridge) | | | | cni0 (bridge) | | |
| | 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 | | |
||
| +----------------+ | | +----------------+ | | +----------------+ | | +----------------+ | |
| +----------------+ | | +----------------+ | | +----------------+ | | +----------------+ | |
||
| | | | | | | | |
| | | | | | | | |
||
Строка 112: | Строка 84: | ||
| | | | | | | | |
| | | | | | | | |
||
| | | | | | | | |
| | | | | | | | |
||
− | +----- |
+ | +----- enp1s0--------------+ +----- enp1s0-------------+ +----- enp1s0-------------+ +----- enp1s0-------------+ |
192.168.0.88 192.168.0.XX 192.168.122.114 192.168.122.YY |
192.168.0.88 192.168.0.XX 192.168.122.114 192.168.122.YY |
||
| | | | |
| | | | |
||
Строка 120: | Строка 92: | ||
</PRE> |
</PRE> |
||
+ | =='''veth/cni0'''== |
||
− | ==Маршрут от pod3 к pod2== |
||
+ | Для каждой ноды (это делается при создании POD, точно так же делает <code>docker</code>) |
||
+ | * Для простоты изображен только один POD (их может быть любое количество) |
||
+ | * При создании POD создается "виртуальный патч-корд", <code>veth</code>, это особый тип виртуального <code>ethernet</code> - два интерфейса соединенных между собой |
||
+ | * Один из пары виртуальных интерфейсов <code>veth</code> помещается в сетевое пространство имен POD (network namespace, так жи иногда используется термин vrf) |
||
+ | * Внутри POD он именуется как <code>eth0</code> (именовать интерфейсы можно любым способом, уникальным имя должно быть только в пределах namespace) |
||
+ | * Таким образом, так как виртуальный патч-корд ведет из POD в сетевую подсистему хост-машины |
||
+ | * Второй конец випртуального патч-корда включен в бридж <code>cni0</code> |
||
+ | |||
+ | =='''Flannel'''== |
||
+ | Интерфейс flannel это <code>VxLAN</code>, проверить это можно командой: |
||
+ | <PRE> |
||
+ | ip -d link show dev flannel.1 |
||
+ | </PRE> |
||
+ | В примере показан интерфейс для ноды '''worker3''' |
||
+ | <PRE> |
||
+ | 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 |
||
+ | </PRE> |
||
+ | Тут нужно обратить внимание на следующие настройки: |
||
+ | * <code>vxlan id 1 </code> VNI=1 (идентификатор сети, нужен если нужно более больше VxLAN) |
||
+ | * <code>local 192.168.122.114 dev enp1s0 </code> Адрес и интерфейс которые используются для отправки пакетов. Так как тут не указан параметр remote то он будет взят из таблицы "коммутации" (условно) |
||
+ | * <code>srcport 0 0 dstport 8472 </code> Порт куда отправлять пакеты UDP |
||
+ | ** <code>srcport 0 0</code> означает что выбор с какого порта оставлен на усмотрение операционной системы (вероятно можно задать отдельный порт или диапазон, но практической необходимости в этом нет) |
||
+ | ** <code>dstport 8472</code> порт получателя - обязательный параметр, и должен быть указан правильно |
||
+ | ** <code>dstport 8472</code> так же является портом на котором ядро будет слушать пакеты для деинкапсуляции, в чем можно убедиться с помощью <code>netstat</code> |
||
+ | |||
+ | <PRE> |
||
+ | root@worker3# netstat -ntplu |
||
+ | </PRE> |
||
+ | Вывод сокращен для удобства чтения, имя процесса ожидаемо отсутствует, так как порт слушает ядро |
||
+ | <PRE> |
||
+ | Active Internet connections (only servers) |
||
+ | Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name |
||
+ | ... |
||
+ | udp 0 0 0.0.0.0:8472 0.0.0.0:* - |
||
+ | </PRE> |
||
+ | <PRE> |
||
+ | root@worker1# netstat -ntplu |
||
+ | </PRE> |
||
+ | <PRE> |
||
+ | Active Internet connections (only servers) |
||
+ | Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name |
||
+ | udp 0 0 0.0.0.0:8472 0.0.0.0:* - |
||
+ | </PRE> |
||
+ | |||
+ | * <code>nolearning </code> Адреса в таблицу "коммутации" добавляются в ручном режиме (в случае с Flannel - как раз процессом flanneld). Кроме ручного режима есть и другие варианты, детали можно прочитать в статье (ссылка в конце) |
||
+ | |||
+ | <BR> |
||
+ | Остальное менее значимо : <code>ttl inherit ageing 300 udpcsum noudp6zerocsumtx noudp6zerocsumrx addrgenmode eui64 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535 </code> |
||
+ | <BR> |
||
+ | <BR> |
||
+ | |||
+ | |||
+ | Для простоты восприятия <code>VxLAN</code> можно думать о нем как о виртуальном свитче распределённом по нескольким физическим нодам, при ээтом в таблице "коммутации" этого "свитча" вместо портов, куда отправлять пакеты для известных адресов, содержаться IP адреса нод |
||
+ | <BR> |
||
+ | Таблица заполняется статически, таким образом эта схема похожа на ситуацию когда на коммутаторе выключен <code>mac-learning</code> и для каждого мака рукам (или сторонним ПО, но не средствами коммутатора) создается запись в таблице коммутации. <BR> |
||
+ | |||
+ | |||
+ | Для сравнения <code>VxLAN</code> и классического коммутатора рассмотрим их таблицы коммутации (<code>fdb</code>, forwarding data base): <BR> |
||
+ | |||
+ | ==VxLan FDB== |
||
+ | Просмотр таблицы коммутации (fdb): |
||
+ | <PRE> |
||
+ | bridge fdb show dev flannel.1 |
||
+ | </PRE> |
||
+ | Для простоты оставлена одна запись (на самом деле их может быть много). |
||
+ | <PRE> |
||
+ | a2:99:54:8b:35:bd dst 192.168.122.88 self permanent |
||
+ | </PRE> |
||
+ | Запись следует читать так: для отправки кадра мак адресу <code>a2:99:54:8b:35:bd</code> в UDP пакете указать получателя <code>dst 192.168.122.88</code> и порт <code>dstport 8472 </code> (порт взят из <code> ip -d dev show .. </code> |
||
+ | |||
+ | ==Cisco (или любой другой коммутатор) FDB== |
||
+ | Просмотр таблицы коммутации: |
||
+ | <PRE> |
||
+ | #show mac address-table |
||
+ | </PRE> |
||
+ | Для простоты оставлена одна запись (на самом деле их может быть много). |
||
+ | <PRE> |
||
+ | Unicast Entries |
||
+ | vlan mac address type protocols port |
||
+ | ---------+---------------+--------+---------------------+------------------------- |
||
+ | 123 00cc.3444.b074 dynamic ip,ipx,assigned,other TenGigabitEthernet1/50 |
||
+ | </PRE> |
||
+ | Для сравнения, на физическом коммутаторе запись читать так:<BR> |
||
+ | для отправки кадра на мак <code> 00cc.3444.b074 </code> в <code>Vlan 123</code> использовать физический порт <code>TenGigabitEthernet1/50</code> |
||
+ | <BR> |
||
+ | В целом аналогия достаточно близкая. |
||
+ | |||
+ | =Разбор прохождения пакетов между двумя POD запущенными на разных нодах= |
||
+ | Далее разберем работу Flannel на примере прохождения пакетов из одного POD запущенного на worker3 на другой запущенный на ноде worker1 |
||
+ | Для простоты буду называть |
||
+ | * pod запущенный на ноде worker3 - '''pod3''' |
||
+ | * pod запущенный на ноде worker1 - '''pod1''' |
||
+ | <BR> |
||
+ | ==Маршрут от pod3 к pod1== |
||
Проверяем как идет маршрутизация из '''pod3''' к адресу '''pod1''': 10.244.1.34 |
Проверяем как идет маршрутизация из '''pod3''' к адресу '''pod1''': 10.244.1.34 |
||
<PRE> |
<PRE> |
||
Строка 132: | Строка 200: | ||
</PRE> |
</PRE> |
||
Из этого маршрута видно три "маршрутизатора" |
Из этого маршрута видно три "маршрутизатора" |
||
+ | 1 - это шлюз для POD (согласно схеме это обычно интерфейс <code>cni0</code> |
||
==Таблица маршрутизации pod3== |
==Таблица маршрутизации pod3== |
||
Строка 163: | Строка 232: | ||
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 |
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 |
||
</PRE> |
</PRE> |
||
+ | |||
+ | После получения пакета на интерфейсе <code>cni0</code> он будет передан дальше согласно имеющимся записям в таблице маршрутизации. |
||
==Таблица маршрутизации на worker3== |
==Таблица маршрутизации на worker3== |
||
Строка 178: | Строка 249: | ||
192.168.122.1 dev enp1s0 proto dhcp scope link src 192.168.122.114 metric 100 |
192.168.122.1 dev enp1s0 proto dhcp scope link src 192.168.122.114 metric 100 |
||
</PRE> |
</PRE> |
||
− | Здесь видно что пакет попадает под строку |
+ | Здесь видно что пакет попадает под строку <code>10.244.1.0/24 via 10.244.1.0</code> и в качестве шлюза будет использован адрес <code>10.244.1.0</code> <BR> |
+ | Тот же результат можно получить воспользовавшись командой <code>ip route get <IP адрес></code> |
||
− | |||
==Маршруты с флагом <code> onlink</code>== |
==Маршруты с флагом <code> onlink</code>== |
||
+ | Найденный в таблице маршрутизации маршрут (с некоторыми особенностями) |
||
<PRE> |
<PRE> |
||
10.244.1.0/24 via 10.244.1.0 dev flannel.1 onlink |
10.244.1.0/24 via 10.244.1.0 dev flannel.1 onlink |
||
Строка 192: | Строка 264: | ||
</PRE> |
</PRE> |
||
<BR> |
<BR> |
||
− | Это означает что для маршрутов с таким флагом '''onlink'''не обязательно иметь адрес назначенный на интерфейсе (в примере это '''flannel.1''')<BR> |
+ | Это означает что для маршрутов с таким флагом '''onlink''' не обязательно иметь адрес назначенный на интерфейсе (в примере это '''flannel.1''')<BR> |
который находился бы в одной сети с шлюзом, через которые отправлять пакеты<BR> |
который находился бы в одной сети с шлюзом, через которые отправлять пакеты<BR> |
||
Строка 208: | Строка 280: | ||
</PRE> |
</PRE> |
||
− | Далее пакет отправляется через устройство <code>flannel.1</code> |
+ | Далее пакет отправляется через устройство <code>flannel.1</code>, для этого есть все необходимые данные: |
+ | * мак-адрес получателя (что бы сформировать <code>ethernet</code>-кадр) |
||
+ | * физический или виртуальный интерфейс через который отправлять сформированный кадр |
||
==Отправка пакета через VxLAN интрерфейс== |
==Отправка пакета через VxLAN интрерфейс== |
||
<BR> |
<BR> |
||
Так как устройство <code>flannel.1</code> является <code>VxLAN</code> то далее работает в чем-то похоже на виртуальный коммутатор, |
Так как устройство <code>flannel.1</code> является <code>VxLAN</code> то далее работает в чем-то похоже на виртуальный коммутатор, |
||
− | <br> таблицу коммутации которого можно просмотреть (оставлена только значимая запись, остальные пропущены): |
+ | <br> таблицу коммутации которого можно просмотреть (оставлена только значимая запись для нужного МАК-адреса, остальные пропущены): |
<PRE> |
<PRE> |
||
bridge fdb show dev flannel.1 |
bridge fdb show dev flannel.1 |
||
Строка 225: | Строка 299: | ||
Тут есть некоторые отличия от обычной таблицы коммутации |
Тут есть некоторые отличия от обычной таблицы коммутации |
||
* "обычные" классические свитчи ищут в таблице какой мак был изучен на каком порту и в каком VLAN |
* "обычные" классические свитчи ищут в таблице какой мак был изучен на каком порту и в каком VLAN |
||
− | * тут таблицу следует читать так: '''что бы отправить кадр на мак получателя (a2:99:54:8b:35:bd) нужно в |
+ | * тут таблицу следует читать так: '''что бы отправить кадр на мак получателя (<code>a2:99:54:8b:35:bd</code>) нужно в IP пакете, в который будем инкапсулировать этот кадр, в качестве получателя указать адрес <code>192.168.122.88</code>''' |
− | * 192.168.122.88 - это адрес ноды '''worker1''' |
+ | * <code>192.168.122.88</code> - это адрес ноды '''worker1''' на которой и расположен нужный POD |
+ | <BR> |
||
+ | Повторю, что записи таблице "коммутации" в реализации <code>Flannel</code> возникают не магическим образом, а их добавляет демон <code>flanneld</code>, который в свою очередь, имея доступ к API <code>kube-apiserver</code> |
||
+ | имеет информацию для заполнения таблички, а именно |
||
+ | * MAC-адреса POD-ов запущенных на ноде |
||
+ | * IP-адрес ноды (<code>kubelet</code>) |
||
+ | Соответственно таблица коммутации на каждой ноде выглядит так (с допущением что на каждой ноде запущен только один POD) |
||
+ | <PRE> |
||
+ | [Мак адрес POD1 на ноде 1] dst [IP адрес ноды 1] |
||
+ | [Мак адрес POD2 на ноде 2] dst [IP адрес ноды 2] |
||
+ | [Мак адрес POD2 на ноде 3] dst [IP адрес ноды 3] |
||
+ | ... |
||
+ | [Мак адрес POD2 на ноде N] dst [IP адрес ноды N] |
||
+ | </PRE> |
||
+ | |||
+ | '''Важное замечание''': |
||
+ | * Такой способ заполнения таблицы не единственно возможный, есть и другие реализации |
||
+ | * Ручное удаление записей из таблицы приводит к тому что данные перестают передаваться (что совершенно ожидаемо) |
||
+ | * Простейший способ восстановить связь после ручного удаления записей - это рестарт пода <code>flanneld</code>на ноде, где были удалены записи. При старте |
||
+ | POD с <code> flanneld </code>актуализирует таблицу "коммутации" интерфейса <code>flannel.1</code>. Данные о том где какой под запущен и какие у них мак-адреса <code>flanneld</code> получает из API k8s |
||
+ | |||
+ | |||
+ | ===Немного детальнее про формат <code>VxLAN</code>=== |
||
+ | Выше есть некоторое упрощение - инкапсуляция проходит в несколько этапов |
||
+ | |||
+ | * фрейм <code>ethernet</code> помещается в UPD |
||
+ | * UDP помещается в IP |
||
+ | |||
+ | Формат пакета <code>VxLAN</code> (https://www.routexp.com/2019/02/vxlan-encapsulation-and-packet-format.html)<BR> |
||
+ | [[Image:VxlanFormat.png|600px]] |
||
+ | |||
+ | ==Передача между нодами== |
||
+ | Далее пакет (с инкапсулированным фреймом) должен быть отправлен на адрес <code>192.168.122.88 </code> (как было выяснено из таблицы "коммутации" интерфейса <code>flannel.1</code>)<BR> |
||
+ | |||
+ | <BR> |
||
+ | Для этого снова обращаемся к таблице маршрутизации хоста '''worker3''': |
||
+ | <PRE> |
||
+ | ip route show |
||
+ | </PRE> |
||
+ | <PRE> |
||
+ | 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 |
||
+ | </PRE> |
||
+ | Здесь видно что сеть <code>192.168.122.0/24</code>, а значит и адрес <code>192.168.122.88</code> доступны через интерфейс <code>enp1s0</code> |
||
+ | <BR> |
||
+ | Это видно из строки <code>192.168.122.0/24 dev enp1s0 proto kernel scope link src 192.168.122.114 </code> или воспользоваться командой <code>ip route get ...</code> |
||
+ | |||
+ | <BR> |
||
+ | Далее происходит классическая отправка пакета через <code>ethernet</code> (без подробностей) |
||
+ | * Выяснить мак получателя (arp) |
||
+ | * сформировать кадр, инкапсулировав в него пакет |
||
+ | * отправить через интерфейс <code>enp1s0 </code> |
||
==Получение пакета на ноде worker1== |
==Получение пакета на ноде worker1== |
||
+ | |||
− | Пакет попадает на ноду '''worker1''', UDP пакет деинкапсулируется и результирующий кадр доставляется на интерфейс <code>flannel.1</code> |
||
+ | Пакет попадает на ноду '''worker1''' так как в сформированном IP |
||
+ | , UDP пакет деинкапсулируется и результирующий кадр доставляется на интерфейс <code>flannel.1</code> |
||
<PRE> |
<PRE> |
||
ifconfig flannel.1 |
ifconfig flannel.1 |
||
Строка 245: | Строка 378: | ||
TX errors 0 dropped 16 overruns 0 carrier 0 collisions 0 |
TX errors 0 dropped 16 overruns 0 carrier 0 collisions 0 |
||
</PRE> |
</PRE> |
||
− | Далее оригинальный пакет извлекается из кадра |
+ | Далее оригинальный пакет извлекается из кадра <code>ethernet</code> и маршрутизируется согласно таблице маршрутизации на '''ноде worker1''' |
+ | |||
==Маршрутизация на ноде worker1== |
==Маршрутизация на ноде worker1== |
||
<PRE> |
<PRE> |
||
+ | ip route show |
||
− | root@worker1:/home/ubuntu# ip r |
||
+ | </PRE> |
||
+ | <PRE> |
||
default via 192.168.122.1 dev enp1s0 proto dhcp src 192.168.122.88 metric 100 |
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.0.0/24 via 10.244.0.0 dev flannel.1 onlink |
||
Строка 258: | Строка 394: | ||
192.168.122.1 dev enp1s0 proto dhcp scope link src 192.168.122.88 metric 100 |
192.168.122.1 dev enp1s0 proto dhcp scope link src 192.168.122.88 metric 100 |
||
</PRE> |
</PRE> |
||
+ | Тут видно что сеть <code>10.244.1.0/24</code> и в том числе и адрес назначения <code>10.244.1.34</code> доступны через интерфейс <code>cni0</code> |
||
− | |||
+ | <BR> |
||
+ | То же самое можно подтвердить командой: |
||
+ | <PRE> |
||
+ | ip route get 10.244.1.34 |
||
+ | </PRE> |
||
<PRE> |
<PRE> |
||
− | root@worker1:/home/ubuntu# ip r get 10.244.1.34 |
||
10.244.1.34 dev cni0 src 10.244.1.1 uid 0 |
10.244.1.34 dev cni0 src 10.244.1.1 uid 0 |
||
cache |
cache |
||
</PRE> |
</PRE> |
||
+ | Зная интерфейс, далее пакет отправляется через него так как через обычный <code>ethernet</code> |
||
− | <PRE> |
||
+ | * Выясняем мак получателя с помощью <code>arp</code> |
||
− | 4: flannel.1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UNKNOWN mode DEFAULT group default |
||
+ | * Формируем и отправляем кадр <code>ethernet</code> конечному получателю |
||
− | 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 |
||
+ | =Полный путь пакет (подведение итога)= |
||
+ | Для закрепления - повторю еще раз (словами, без деталей) путь пакета: |
||
+ | # '''Внутри POD3:''' Ядро просматривает таблицу маршрутизации, находит маршрут (это default gateway, 10.244.3.1) |
||
− | </PRE> |
||
+ | # '''Внутри POD3:''' Определяется интерфейс через который доступен default gateway (это тот конец veth который заведен в POD3) |
||
+ | # '''Внутри POD3:''' Пакет отправляется на шлюз POD3 (мак адрес шлюза известен через ARP, MAC-адрес получателя в пакете это мак адрес шлюза, IP адрес получателя в пакете это IP адрес POD1) |
||
+ | # '''Нода Worker3:''' Пакет проходит через <code>veth</code> и попадает в Global Network Namespace (т.е. в основной сетевой стек) |
||
+ | # '''Нода Worker3:''' Так как в основном сетевом стеке второй конец <code>veth</code> включен в бридж <code>cni0</code> то входящим логическим интерфейсом будет <code>cni0</code> (хотя физическим - <code>vethx</code>) |
||
+ | # '''Нода Worker3:''' Пакет имеет правильный мак-адрес получателя, совпадающий с мак-адресом интерфейса <code>cni0</code>, по этой причине пакет не будет отброшен, а будет передан на обработку процессу маршрутизации |
||
+ | # '''Нода Worker3:''' В процесса маршрутизации определяется что адрес получателя не является локальным и доступен через интерфейс <code>flannel.1</code> через шлюз <code>10.244.1.0</code> |
||
+ | # '''Нода Worker3:''' Мак-адрес шлюза 10.244.1.0 определяется с помощью ARP |
||
+ | # '''Нода Worker3:''' Формируется новый пакет, который имеет мак-адресом получателя мак-адрес шлюза 10.244.1.0, изученный через arp, а IP адресом получателя - адрес POD1 |
||
+ | # '''Нода Worker3:''' Пакет попадает в интерфейс <code>flannel.1</code> на Worker 3 и обрабатывается согласно правилам коммутации которые настроены на flannel.1. Напомню что каждый <code>VxLAN</code> можно считать распределённым коммутатором, со своей таблицей коммутации (<code>FDB</code>) |
||
+ | # '''Нода Worker3:''' Согласно этих правил пакет инкапсулируется в UDP и отправляется на адрес Worker 1 ( 192.168.0.88 ) |
||
+ | # '''Нода Worker3:''' Отправка пакета на Worker 1 происходит обычным образом - просмотр таблицы маршрутизации, определение интерфейса, получение мак-адреса с помощью <code>arp</code>, отправка пакета |
||
+ | # '''Нода Worker3:''' Инкапсулированный пакет, содержащий внутри себя Etherneth-frame доставлен на интерфейс <code>enp1s0</code> ноды Worker 1 (процесс доставки, коммутация зависит от физической сети и подразумевает ее работоспособность) |
||
+ | # '''Нода Worker1:''' Так как это пакет на порт, на котором ожидается получения пакетов с инкапсуляцией <code>VxLAN</code>, (см. <code>netstat -ntplu</code>), то пакет проходит процедуру деинкапсуляции и оригинальный пакет попадает на интерфейс flannel.1 на ноде worker 1 |
||
+ | # '''Нода Worker1:''' Согласно таблице маршрутизации определяется что получатель этого пакета доступен через интерфейс <code>cni0</code> |
||
+ | # '''Нода Worker1:''' Происходит заполнение записи ARP для получателя обычным образом |
||
+ | # '''Нода Worker1:'''Пакет отправлется с интерфейса <code>cni0</code>. Так как это интерфейс типа бридж, то процесс коммутации опердеяет на каком порту виртуального бриджа доступен мак-адрес получателя. Этот адрес уже известен и изучен процессом комутации так как он учавствовал в процессе поределения ARP. Если нет то пакет был бы отправлен на все порты |
||
+ | # '''Нода Worker1:'''Пакет коммутируется через виртуальный интерфейс <code>veth</code> и достигает pod1 |
||
+ | |||
+ | =Результат= |
||
+ | Оригинальный пакет через оверлейную сеть доставлен получателю. |
||
+ | |||
+ | =Что дальше= |
||
+ | <code>Calico</code> ? |
||
+ | =Интересно= |
||
+ | * https://vincent.bernat.ch/en/blog/2017-vxlan-linux |
Текущая версия на 09:25, 8 октября 2024
Flannel
Flannel
- один из способов организации оверлейной сети
Зачем это нужно
- В частных случаях когда сеть полностью контролируема и можно назначать любые маршруты и адреса на всех устройствах - не нужно строить никаких оверлейных сетей
- Если сеть не контролируется (например в арендованном датацентре или облаке, и не возможно создание маршрутов между worker-nodes то это один из множества возможных способов обеспечить оверлейную сеть
Как это устроено
"под капотом" используется VxLAN
Подробнее про VxLAN
"VxLAN
это не только тунель но и свитч" =) точнее сказать распределенный свитч
VxLAN
инкапсулирует кадры Ethernet
уровня 2 в пакеты UDP уровня 4, что означает, что виртуальные подсети уровня 2 могут охватывать базовые сети уровня.
Другими словами, это дает возможность строить "продолжение" сети 2 уровня поверх сети третьего уровня,
например соединить 2 географически разнесенных сегмента сети, между которыми нет физического
соединения (а только логическое, через интернет или другую сеть с маршрутизацией а не коммутацией)
так, что бы с точки зрения подключенных устройств это выглядело бы как-будто они включены в
один сегмент (например в один свитч, или для бытового уровня - в один домашний роутер).
VxLAN
помешает кадры ethernet
внутрь пакета 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
, это особый тип виртуальногоethernet
- два интерфейса соединенных между собой - Один из пары виртуальных интерфейсов
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
Порт куда отправлять пакеты UDPsrcport 0 0
означает что выбор с какого порта оставлен на усмотрение операционной системы (вероятно можно задать отдельный порт или диапазон, но практической необходимости в этом нет)dstport 8472
порт получателя - обязательный параметр, и должен быть указан правильноdstport 8472
так же является портом на котором ядро будет слушать пакеты для деинкапсуляции, в чем можно убедиться с помощьюnetstat
root@worker3# netstat -ntplu
Вывод сокращен для удобства чтения, имя процесса ожидаемо отсутствует, так как порт слушает ядро
Active Internet connections (only servers) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name ... udp 0 0 0.0.0.0:8472 0.0.0.0:* -
root@worker1# netstat -ntplu
Active Internet connections (only servers) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name udp 0 0 0.0.0.0:8472 0.0.0.0:* -
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
и классического коммутатора рассмотрим их таблицы коммутации (fdb
, forwarding data base):
VxLan FDB
Просмотр таблицы коммутации (fdb):
bridge fdb show dev flannel.1
Для простоты оставлена одна запись (на самом деле их может быть много).
a2:99:54:8b:35:bd dst 192.168.122.88 self permanent
Запись следует читать так: для отправки кадра мак адресу a2:99:54:8b:35:bd
в UDP пакете указать получателя dst 192.168.122.88
и порт dstport 8472
(порт взят из ip -d dev show ..
Cisco (или любой другой коммутатор) FDB
Просмотр таблицы коммутации:
#show mac address-table
Для простоты оставлена одна запись (на самом деле их может быть много).
Unicast Entries vlan mac address type protocols port ---------+---------------+--------+---------------------+------------------------- 123 00cc.3444.b074 dynamic ip,ipx,assigned,other TenGigabitEthernet1/50
Для сравнения, на физическом коммутаторе запись читать так:
для отправки кадра на мак 00cc.3444.b074
в Vlan 123
использовать физический порт TenGigabitEthernet1/50
В целом аналогия достаточно близкая.
Разбор прохождения пакетов между двумя POD запущенными на разных нодах
Далее разберем работу Flannel на примере прохождения пакетов из одного POD запущенного на worker3 на другой запущенный на ноде worker1 Для простоты буду называть
- pod запущенный на ноде worker3 - pod3
- pod запущенный на ноде worker1 - pod1
Маршрут от pod3 к pod1
Проверяем как идет маршрутизация из 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
и в качестве шлюза будет использован адрес 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
, для этого есть все необходимые данные:
- мак-адрес получателя (что бы сформировать
ethernet
-кадр) - физический или виртуальный интерфейс через который отправлять сформированный кадр
Отправка пакета через 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 с flanneld
актуализирует таблицу "коммутации" интерфейса flannel.1
. Данные о том где какой под запущен и какие у них мак-адреса flanneld
получает из API k8s
Немного детальнее про формат VxLAN
Выше есть некоторое упрощение - инкапсуляция проходит в несколько этапов
- фрейм
ethernet
помещается в UPD - UDP помещается в IP
Формат пакета VxLAN
(https://www.routexp.com/2019/02/vxlan-encapsulation-and-packet-format.html)
Передача между нодами
Далее пакет (с инкапсулированным фреймом) должен быть отправлен на адрес 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
Далее оригинальный пакет извлекается из кадра ethernet
и маршрутизируется согласно таблице маршрутизации на ноде 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
- Формируем и отправляем кадр
ethernet
конечному получателю
Полный путь пакет (подведение итога)
Для закрепления - повторю еще раз (словами, без деталей) путь пакета:
- Внутри POD3: Ядро просматривает таблицу маршрутизации, находит маршрут (это default gateway, 10.244.3.1)
- Внутри POD3: Определяется интерфейс через который доступен default gateway (это тот конец veth который заведен в POD3)
- Внутри POD3: Пакет отправляется на шлюз POD3 (мак адрес шлюза известен через ARP, MAC-адрес получателя в пакете это мак адрес шлюза, IP адрес получателя в пакете это IP адрес POD1)
- Нода Worker3: Пакет проходит через
veth
и попадает в Global Network Namespace (т.е. в основной сетевой стек) - Нода Worker3: Так как в основном сетевом стеке второй конец
veth
включен в бриджcni0
то входящим логическим интерфейсом будетcni0
(хотя физическим -vethx
) - Нода Worker3: Пакет имеет правильный мак-адрес получателя, совпадающий с мак-адресом интерфейса
cni0
, по этой причине пакет не будет отброшен, а будет передан на обработку процессу маршрутизации - Нода Worker3: В процесса маршрутизации определяется что адрес получателя не является локальным и доступен через интерфейс
flannel.1
через шлюз10.244.1.0
- Нода Worker3: Мак-адрес шлюза 10.244.1.0 определяется с помощью ARP
- Нода Worker3: Формируется новый пакет, который имеет мак-адресом получателя мак-адрес шлюза 10.244.1.0, изученный через arp, а IP адресом получателя - адрес POD1
- Нода Worker3: Пакет попадает в интерфейс
flannel.1
на Worker 3 и обрабатывается согласно правилам коммутации которые настроены на flannel.1. Напомню что каждыйVxLAN
можно считать распределённым коммутатором, со своей таблицей коммутации (FDB
) - Нода Worker3: Согласно этих правил пакет инкапсулируется в UDP и отправляется на адрес Worker 1 ( 192.168.0.88 )
- Нода Worker3: Отправка пакета на Worker 1 происходит обычным образом - просмотр таблицы маршрутизации, определение интерфейса, получение мак-адреса с помощью
arp
, отправка пакета - Нода Worker3: Инкапсулированный пакет, содержащий внутри себя Etherneth-frame доставлен на интерфейс
enp1s0
ноды Worker 1 (процесс доставки, коммутация зависит от физической сети и подразумевает ее работоспособность) - Нода Worker1: Так как это пакет на порт, на котором ожидается получения пакетов с инкапсуляцией
VxLAN
, (см.netstat -ntplu
), то пакет проходит процедуру деинкапсуляции и оригинальный пакет попадает на интерфейс flannel.1 на ноде worker 1 - Нода Worker1: Согласно таблице маршрутизации определяется что получатель этого пакета доступен через интерфейс
cni0
- Нода Worker1: Происходит заполнение записи ARP для получателя обычным образом
- Нода Worker1:Пакет отправлется с интерфейса
cni0
. Так как это интерфейс типа бридж, то процесс коммутации опердеяет на каком порту виртуального бриджа доступен мак-адрес получателя. Этот адрес уже известен и изучен процессом комутации так как он учавствовал в процессе поределения ARP. Если нет то пакет был бы отправлен на все порты - Нода Worker1:Пакет коммутируется через виртуальный интерфейс
veth
и достигает pod1
Результат
Оригинальный пакет через оверлейную сеть доставлен получателю.
Что дальше
Calico
?